`

CopyOnWriteArrayList 实现原理与应用

 
阅读更多

CopyOnWriteArrayList

一、简介

    JDK5中添加了新的concurrent包,其中包含了很多并发容器,这些容器针对多线程环境进行了优化,大大提高了容器类在并发环境下的执行效率。

    CopyOnWriteArrayList类是一个线程安全的List接口的实现,在该类的内部进行元素的写操作时,底层的数组将被完整的复制,这对于读 操作远远多于写操作的应用非常适合。在CopyOnWriteArrayList上进行操作时,读操作不需要加锁,而写操作类实现中对其进行了加锁。

二、具体实现

    CopyOnWriteArrayList底层的定义如下:

 

[java] view plain copy
  1. public   class  CopyOnWriteArrayList<E>     
  2.         implements  List<E>, RandomAccess, Cloneable, java.io.Serializable {     
  3.     
  4.     private   volatile   transient  E[] array;     
  5.     
  6.     private  E[] array() {  return  array; }     
  7.     
  8.     // 该操作是加锁的,防止array在copy的时候被替换       
  9.     private   synchronized   void  copyIn(E[] toCopyIn,  int  first,  int  n) {     
  10.         array  = (E[]) new  Object[n];     
  11.         System.arraycopy(toCopyIn, first, array, 0 , n);     
  12.     }     
  13.     
  14.   ...     
  15. }  


读写操作:

[java] view plain copy
  1. public   class  CopyOnWriteArrayList<E>     
  2.         implements  List<E>, RandomAccess, Cloneable, java.io.Serializable {     
  3.     
  4.     public  E get( int  index) {     
  5.      // 由于包括rangeCheck和index两个操作,并不是直接在array上执行      
  6.    // 而是使用本地变量elementData引用array数组,防止两个操作之间      
  7.    // array被替换      
  8.      E[] elementData = array();     
  9.         rangeCheck(index, elementData.length);     
  10.         return  elementData[index];     
  11.     }     
  12.          
  13.     public   synchronized  E set( int  index, E element) {  // 是同步的      
  14.         int  len = array.length;     
  15.         rangeCheck(index, len);     
  16.         E oldValue = array[index];     
  17.     
  18.         // 判断该写的元素与原数据是否相同      
  19.         boolean  same = (oldValue == element ||     
  20.         (element != null  && element.equals(oldValue)));     
  21.              
  22.         if  (!same) {     
  23.             // [1] 创建一个新数组,将原array的值拷贝至新数组      
  24.         E[] newArray = (E[]) new  Object[len];     
  25.             System.arraycopy(array, 0 , newArray,  0 , len);     
  26.             // [2] set的元素      
  27.         newArray[index] = element;     
  28.             // [3] 替换底层array数组      
  29.         array = newArray;     
  30.         }     
  31.         return  oldValue;     
  32.     }     
  33.     
  34.   ...     
  35. }    


add和remove也采用相同的技术:

[java] view plain copy
  1. public   class  CopyOnWriteArrayList<E>     
  2.         implements  List<E>, RandomAccess, Cloneable, java.io.Serializable {     
  3.     
  4.     public   synchronized   boolean  add(E element) {     
  5.         // [1] new and copy      
  6.         int  len = array.length;     
  7.         E[] newArray = (E[]) new  Object[len+ 1 ];     
  8.         System.arraycopy(array, 0 , newArray,  0 , len);     
  9.         // [2] add element      
  10.         newArray[len] = element;     
  11.         // [3] change base array      
  12.         array = newArray;     
  13.         return   true ;     
  14.     }     
  15.     
  16.     public   synchronized  E remove( int  index) {     
  17.         int  len = array.length;     
  18.         rangeCheck(index, len);     
  19.         E oldValue = array[index];     
  20.         // new一个新的数组      
  21.       E[] newArray = (E[]) new  Object[len- 1 ];     
  22.         // copy index之前的元素      
  23.       System.arraycopy(array, 0 , newArray,  0 , index);     
  24.         // copy余下的元素      
  25.       int  numMoved = len - index -  1 ;     
  26.         if  (numMoved >  0 )     
  27.             System.arraycopy(array, index+1 , newArray, index, numMoved);     
  28.         // 替换array引用          
  29.      array = newArray;     
  30.         return  oldValue;     
  31.     }     
  32.     
  33.   ...     
  34. }    


特别注意:在CopyOnWriteArrayList上获得的Iterator是不能进行set和remove操作的,否则会抛出异常。

 

除了加锁外,其实还有一种方式可以防止并发修改异常,这就是将读写分离技术(不是数据库上的)。

先回顾一下一个常识:

1、JAVA中“=”操作只是将引用和某个对象关联,假如同时有一个线程将引用指向另外一个对象,一个线程获取这个引用指向的对象,那么他们之间不会发生ConcurrentModificationException,他们是在虚拟机层面阻塞的,而且速度非常快,几乎不需要CPU时间。

2、JAVA中两个不同的引用指向同一个对象,当第一个引用指向另外一个对象时,第二个引用还将保持原来的对象。

 

基于上面这个常识,我们再来探讨下面这个问题:

在CopyOnWriteArrayList里处理写操作(包括add、remove、set等)是先将原始的数据通过JDK1.6的Arrays.copyof()来生成一份新的数组

然后在新的数据对象上进行写,写完后再将原来的引用指向到当前这个数据对象(这里应用了常识1),这样保证了每次写都是在新的对象上(因为要保证写的一致性,这里要对各种写操作要加一把锁,JDK1.6在这里用了重入锁),

然后读的时候就是在引用的当前对象上进行读(包括get,iterator等),不存在加锁和阻塞,针对iterator使用了一个叫 COWIterator的阉割版迭代器,因为不支持写操作,当获取CopyOnWriteArrayList的迭代器时,是将迭代器里的数据引用指向当前 引用指向的数据对象,无论未来发生什么写操作,都不会再更改迭代器里的数据对象引用,所以迭代器也很安全(这里应用了常识2)。

CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差,但是读操作因为操作的对象和写操作不是同一个对象,读之 间也不需要加锁,读和写之间的同步处理只是在写完后通过一个简单的“=”将引用指向新的数组对象上来,这个几乎不需要时间,这样读操作就很快很安全,适合 在多线程里使用,绝对不会发生ConcurrentModificationException ,所以最后得出结论:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

分享到:
评论

相关推荐

    深入解析Java中的CopyOnWriteArrayList:工作原理与应用

    本文将详细探讨 CopyOnWriteArrayList 的工作原理、优缺点、适用场景以及代码示例。 CopyOnWriteArrayList 是一种适用于读多写少场景的线程安全集合,通过写时复制机制提供了高效的读性能和线程安全性。然而,由于写...

    Java并发编程原理与实战

    线程之间通信之join应用与实现原理剖析.mp4 ThreadLocal 使用及实现原理.mp4 并发工具类CountDownLatch详解.mp4 并发工具类CyclicBarrier 详解.mp4 并发工具类Semaphore详解.mp4 并发工具类Exchanger详解.mp4 ...

    Java的CopyOnWriteArrayList功能详解,及无法保证数据是实时同步.docx

    与`Vector`相比,`CopyOnWriteArrayList`在性能上有显著优势。`Vector`通过在每个方法上使用`synchronized`关键字实现了线程安全,但这导致了较高的锁粒度,即每次修改都需要获取全局锁,严重影响了并发性能。例如,...

    基于CopyOnWriteArrayList并发容器(实例讲解)

    CopyOnWriteArrayList是一种高效的并发容器,可以在多线程环境中高效的进行读取和写入操作,它的实现原理是基于Copy-On-Write机制的,可以应用于高并发的缓存系统和其他需要高效读取和写入的场景中。

    Java 多线程与并发(14-26)-JUC集合- CopyOnWriteArrayList详解.pdf

    - **增删改查实现原理**:`CopyOnWriteArrayList`的主要特点是所有的写入操作(如`add`、`set`等)都会创建一个新数组,将原数组中的数据复制到新数组,并在新数组中进行修改,最后用新数组替换旧数组。这种方法虽然...

    龙果 java并发编程原理实战

    第35节线程之间通信之join应用与实现原理剖析00:10:17分钟 | 第36节ThreadLocal 使用及实现原理00:17:41分钟 | 第37节并发工具类CountDownLatch详解00:22:04分钟 | 第38节并发工具类CyclicBarrier 详解00:11:52...

    深入Java集合学习系列(三):ArrayList实现原理

    Java集合框架中,ArrayList是一种常见的集合实现类,用于存储和操作对象集合。ArrayList基于动态数组的...开发者在使用ArrayList时,应当清楚地理解其内部实现原理和相关特性,以便在不同的应用场景中做出正确的选择。

    Java 并发编程原理与实战视频

    第35节线程之间通信之join应用与实现原理剖析00:10:17分钟 | 第36节ThreadLocal 使用及实现原理00:17:41分钟 | 第37节并发工具类CountDownLatch详解00:22:04分钟 | 第38节并发工具类CyclicBarrier 详解00:11:52...

    Java concurrency集合之 CopyOnWriteArrayList_动力节点Java学院整理

    CopyOnWriteArrayList实现了List接口,因此它是一个队列。它包含了成员lock,每一个CopyOnWriteArrayList都和一个互斥锁lock绑定,通过lock,实现了对CopyOnWriteArrayList的互斥访问。CopyOnWriteArrayList包含了...

    9、并发容器(Map、List、Set)实战及其原理.pdf

    - **基于CopyOnWriteArrayList实现**:`CopyOnWriteArraySet`是基于`CopyOnWriteArrayList`实现的,继承了后者的所有优点。 - **去重机制**:在添加元素时,会调用`CopyOnWriteArrayList`的`addIfAbsent`方法来确保...

    struts2的运行原理

    `CopyOnWriteArrayList`是Java并发编程中一种特殊的列表实现,它通过复制数组实现线程安全,适合于读多写少的并发场景。在实际开发中,理解这些概念和它们的工作原理,有助于构建更稳定、高效的Java Web应用。

    CopyOnWriteArrayListCopyOnWri

    CopyOnWriteArraySet是基于CopyOnWriteArrayList实现的一个线程安全的集合,它继承自AbstractSet并实现了Set接口。CopyOnWriteArraySet与CopyOnWriteArrayList的主要区别在于,它只存储不重复的元素,因此在添加元素...

    数据库连接池原理

    资源池的实现方式多种多样,例如DBCP使用`LinkedBlockingDeque`,Druid使用基于锁控制的数组,Tomcat-JDBC使用`FairBlockingQueue`,而HikariCP则使用`ThreadLocal`加上`CopyOnWriteArrayList`。 ##### 2.3 获取...

    Java后端体系高级面试题

    - HashSet与TreeSet的实现原理 - CopyOnWriteArrayList与ConcurrentSkipListMap的应用 3. **多线程**: - 线程的创建方式:实现Runnable接口、继承Thread类 - 同步机制:synchronized关键字,volatile变量 - ...

    高性能java系统实现与调优

    ### 高性能Java系统实现与调优 #### 核心概念与原则 - **木桶原理**:在高性能系统的构建中,系统的整体性能受到最短板(即性能最低的部分)的限制。因此,优化的重点在于识别并提升这些瓶颈,确保整个系统能够...

    Java并发包源码分析(JDK1.8)

    AQS相关应用(CountDownLatch、CyclicBarrier、Semaphore等),executor(ThreadPoolExecutor、ScheduledThreadPoolExecutor、FutureTask等),collection(ConcurrentHashMap、CopyOnWriteArrayList等), ...

    java集合PDF汇总

    ArrayList实现原理_尚硅谷_张晓飞.pdf"可能是重复的,但再次强调ArrayList的实现原理:ArrayList的线程安全性较差,如果在并发环境下操作,需要配合synchronized关键字或者使用并发集合如CopyOnWriteArrayList。...

    龙果java并发编程完整视频

    第35节线程之间通信之join应用与实现原理剖析00:10:17分钟 | 第36节ThreadLocal 使用及实现原理00:17:41分钟 | 第37节并发工具类CountDownLatch详解00:22:04分钟 | 第38节并发工具类CyclicBarrier 详解00:11:52...

Global site tag (gtag.js) - Google Analytics