导航

Java 集合 线程安全

Posted on 2017-08-10 20:32  耍流氓的兔兔  阅读(295)  评论(0编辑  收藏  举报

 

Java中常用的集合框架中的实现类HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList、HashMap、TreeMap都是线程不安全的,如果多个线程同时访问它们,而且有超过一个的线程试图修改它们,则存在线程安全的问题。

Hashtable:

  Hashtable是线程安全的,任意时刻只能有一个线程对Hashtable进行操作,并发性不如ConcurrentHashMap,因为后者引入了分段锁

  Hashtable的线程安全使用的是一个单独的全部Map范围的锁,这个锁在所有的插入、删除、查询操作中都会持有,甚至在使用Iterator遍历整个Map时也会持有这个单独的锁。当锁被一个线程持有时,就能够防止其他线程访问该Map,即便其他线程都处于闲置状态。这种单个锁机制极大的限制了并发的性能

  参考博客:http://blog.csdn.net/zcc_0015/article/details/46932667

Collections.sychroinzedXxx():

  Collections提供了多个synchronizedXxx()方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题

        Collections.synchronizedMap(map);
        Collections.synchronizedList(list);
        Collections.synchronizedSet(set);

  以synchronizedMap()为例,它的方法实现如下,接受一个map对象,返回一个SynchronizedMap<>对象

    public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<>(m);
    }

   我们再来看SynchronizedMap<>类,它实现了Map<K,V>接口,并且是可序列化的,它有两个构造方法,而且使用了synchronized来保证对Map的操作是线程安全的

 private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

        SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        }

        public int size() {
            synchronized (mutex) {return m.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return m.isEmpty();}
        }
        public boolean containsKey(Object key) {
            synchronized (mutex) {return m.containsKey(key);}
        }
        public boolean containsValue(Object value) {
            synchronized (mutex) {return m.containsValue(value);}
        }
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

        public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }
        public V remove(Object key) {
            synchronized (mutex) {return m.remove(key);}
        }
        public void putAll(Map<? extends K, ? extends V> map) {
            synchronized (mutex) {m.putAll(map);}
        }
        public void clear() {
            synchronized (mutex) {m.clear();}
        }
  }

   但是我们进一步考虑,这里的get、set、remove等方法是同步的,也就是说我们对map的操作是线程安全的,但是它们之间呢?比如说:用户A对map进行了get操作,准备remove map中的元素;而此时,用户B也对map进行get操作,然后remove了这一项,此时,用户A开始做remove的时候,发现这个元素已经不存在了,这段操作显然不是线程安全的,我们显然需要将整个map锁住,这样的话,效率就会大大降低

ConcurrentHashMap:

  Java5新增了ConcurrentMap接口和它的一个实现类ConcurrentHashMap,ConcurrentHashMap提供了和HashTable、SynchronizedMap种不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的

  ConcurrentHashMap抛弃了HashTable的单锁机制,使用了锁分离技术,使得多个修改操作能够并发进行。ConcurrentHashMap内部使用段(Segment),默认是16个段,来表示这些不同的部分,每个段其实就是一个小的hash table,即通过多个锁来控制对不同段的hash表的修改,每个锁只负责一部分key的hash值范围。只要多个修改操作发生在不同的段上,它们就可以并发进行

CopyOnWrite:

  同步的实例方法在执行之前都会隐式的需要一个锁。java中 也可以显示的调用锁,CopyOnWrite容器就是用ReentrantLock锁实现的,ReentrantLock是为创建相互排斥的锁的Lock的具体实现

  CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器

  参考链接:http://blog.csdn.net/mark_wk/article/details/22588263

         https://zhidao.baidu.com/question/1692121547117149828.html

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }