zoukankan      html  css  js  c++  java
  • Java 集合 线程安全

    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();
            }
        }
  • 相关阅读:
    C# 从服务器下载文件
    不能使用联机NuGet 程序包
    NPOI之Excel——合并单元格、设置样式、输入公式
    jquery hover事件中 fadeIn和fadeOut 效果不能及时停止
    UVA 10519 !! Really Strange !!
    UVA 10359 Tiling
    UVA 10940 Throwing cards away II
    UVA 10079 Pizze Cutting
    UVA 763 Fibinary Numbers
    UVA 10229 Modular Fibonacci
  • 原文地址:https://www.cnblogs.com/roxy/p/7341270.html
Copyright © 2011-2022 走看看