zoukankan      html  css  js  c++  java
  • JAVA提高二十:CopyOnWriteArrayList&CopyOnWriteArraySet&ConcurrentHashMap介绍

    前面我们将java集合类的大部分类都进行了深入分析,但我们会发现一个共性问题就是并发的问题,那么如何解决呢?我们前面基本都是通过Collections的一个工具类来进行的解决,但实际大部分使用中人们普遍会使用并发的容器,在JDK1.5之后,针对基于散列的Map,提供了新的ConcurrentHashMap,针对迭代需求的list,提供了CopyOnWriteList.。因此这里我进行下简单介绍和分析,具体的原理实现将在并发学习中进行详细的介绍。

    一、集合总结

    我们前面学习了很多集合类,这里我们做一个简单的总结,先整体了解类的关系,如下图所示:

    大部分我们应该都有过介绍了,下面我们回顾一些重点知识:

    关  注  点 结      论
    ArrayList是否允许空 允许
    ArrayList是否允许重复数据 允许
    ArrayList是否有序 有序
    ArrayList是否线程安全 非线程安全
    关  注  点 结      论
    LinkedList是否允许空 允许
    LinkedList是否允许重复数据 允许
    LinkedList是否有序 有序
    LinkedList是否线程安全 非线程安全
    关  注  点 结      论
    HashMap是否允许空 Key和Value都允许为空
    HashMap是否允许重复数据 Key重复会覆盖、Value允许重复
    HashMap是否有序 无序,特别说明这个无序指的是遍历HashMap的时候,得到的元素的顺序基本不可能是put的顺序
    HashMap是否线程安全 非线程安全
    关  注  点 结      论
    LinkedHashMap是否允许键值对为空 Key和Value都允许空
    LinkedHashMap是否允许重复数据 Key重复会覆盖、Value允许重复
    LinkedHashMap是否有序 有序
    LinkedHashMap是否线程安全 非线程安全

    二、CopyOnWriteArrayList&CopyOnWriteArraySet&ConcurrentHashMap

    1.引言
      在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的。在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操作,都添加synchronized来进行同步,此种方式尽管简单,但是其性能是非常地下的,所以现在已经不怎么使用了。人们普遍会使用并发的容器,在JDK1.5之后,针对基于散列的Map,提供了新的ConcurrentHashMap,针对迭代需求的list,提供了CopyOnWriteList.
    2.ConcurrentHashMap
      ConcurrentHashMap使用了一种分段锁的策略,使得map可以被多个读写线程并行的访问。基本可以认为是将map的key值范围分为多个段,这样多个线程访问的时候,他们需要访问的key值在不同的段,所以可以互相不干扰,
    使用不同的锁对象来进行并发操作。
      ConcurrentHashMap在使用迭代器遍历的时候,不会报ConcurrentModificationException,提供“弱一致性”。在遍历迭代的时候,也会反应出在迭代器创建之后的数据修改。
    应用场景
    针对一般的有并发需求的map,都应该使用ConcurrentHashMap. 它的性能优于Hashtable和synchronizedMap。
      缺点
    1.不是强一致性 
      由于是采用的分段锁策略,所以一些数据不能保证强一致性。比如针对容器的size方法,由于线程A只是获得了自己的分段锁,它不能保证其他线程对容器的修改,所以此时线程A可能使用size,会得到不稳定数据。这种情况下,是对同步性能的一些折衷。如果业务需求必须满足强一致性,才会需要对整个Map进行锁操作。并发容器的弱一致性的概念背景,是在高并发情况下,容器的size和isEmpty之类的方法,用处不大,所以可以忍受数据不一致性。
    3.CopyOnWrite容器
      在JDK1.5之后,java.util.concurrent引入了两个CopyOnWrite容器,分别是CopyOnWriteArrayList, CopyOnWriteArraySet.
      顾名思义,CopyOnWrite就是在write操作之前,对集合进行Copy,针对容器的任意改操作(add,set,remove之类),都是在容器的副本上进行的。并且在修改完之后,将原容器的引用指向修改后的副本。
    如果线程A得到容器list1的iterator之后,线程B对容器list1加入了新的元素,由于线程A获得list1的iterator时候在线程B对list1进行修改前,所以线程A是看不到线程B对list1进行的任何修改。
    具体到源码,看一下add操作

     /**
         * Appends the specified element to the end of this list.
         *
         * @param e element to be appended to this list
         * @return {@code true} (as specified by {@link Collection#add})
         */
        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();
            }
        }

    可以发现,写操作是会有个锁lock.lock(),这保证了多线程写操作之间的同步。之后使用Arrays.copyOf来进行数组拷贝,在修改完成后,setArray(newElements)将原来的数组引用指向新的数组。
      应用场景
    经常用在读多写少的场景,比如EventListener的添加,网站的category列表等偶尔修改,但是需要大量读取的情景。
      缺点
    1.数据一致性的问题。  
      因为读操作没有用到并发控制,所以可能某个线程读到的数据不是实时数据。
    2.内存占用问题。
      因为写操作会进行数据拷贝,并且旧有的数据引用也可能被其他线程占有一段时间,这样针对数据比较大的情况,可能会占用相当大的内存。并且由于每次写操作都会占用额外的内存,最后进行的GC时间也可能相应的增加。

    最后,集合的介绍知识到此结束,感谢博客园的各位园友们,下面的学习将进入IO/Nio的学习。

    参考资料:

    http://wiki.jikexueyuan.com/project/java-collection/linkedhashset.html
    https://www.cnblogs.com/skywang12345
    http://blog.csdn.net/column/details/java-collections.html
    https://www.cnblogs.com/xrq730
    http://www.cnblogs.com/xiaoxi/category/929860.html

  • 相关阅读:
    131. Palindrome Partitioning
    130. Surrounded Regions
    129. Sum Root to Leaf Numbers
    128. Longest Consecutive Sequence
    125. Valid Palindrome
    124. Binary Tree Maximum Path Sum
    122. Best Time to Buy and Sell Stock II
    121. Best Time to Buy and Sell Stock
    120. Triangle
    119. Pascal's Triangle II
  • 原文地址:https://www.cnblogs.com/pony1223/p/8004721.html
Copyright © 2011-2022 走看看