前言
今天咱们一块儿学习下java.util.concurrent并发包里的CopyOnWriteArrayList工具类。当有多个线程可能同时遍历、修改某个公共数组时候,若是不但愿因使用synchronize关键字锁住整个数组而影响性能,能够考虑使用CopyOnWriteArrayList。html
CopyOnWriteArrayList API
CopyOnWriteArrayList的定义以下:java
public class CopyOnWriteArrayList<E> extends Object implements List<E>, RandomAccess, Cloneable, Serializable
它也属于Java集合框架的一部分,是ArrayList的线程安全的变体,跟ArrayList的不一样在于:CopyOnWriteArrayList针对数组的修改操做(add、set等)是基于内部拷贝的一份数据而进行的。换句话说,即便在一个线程进行遍历操做时有其余线程可能进行插入或删除操做,咱们也能够“线程安全”得遍历CopyOnWriteArrayList。面试
例子1:插入(删除)数据的同时进行遍历
CopyOnWriteArrayList的实现原理是,在一个线程开始遍历(建立Iterator对象)时,内部会建立一个“快照”数组,遍历基于这个快照Iterator进行,在遍历过程当中这个快照数组不会改变,也就不会抛出ConcurrentModificationException
。若是在遍历的过程当中有其余线程尝试改变数组的内容,就会拷贝一份新的数据进行变动,然后面再来访问这个数组的线程,看到的就是变动过的数组。后端
-
建立一个CopyOnWriteArrayList数组numbers;api
CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});
-
建立一个遍历器iterator;数组
Iterator<Integer> iterator = numbers.iterator();
-
给numbers中增长(或删除、修改)一个元素;缓存
numbers.add(100);
-
利用iterator遍历数组的元素,发现遍历的结果是Iterator对象建立以前的;安全
List<Integer> result = new LinkedList<>(); iterator.forEachRemaining(result::add); assertThat(result).containsOnly(1, 3, 5, 78);
完整的例子以下:并发
package org.java.learn.concurrent.copyonwritearraylist; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import static org.assertj.core.api.Assertions.*; /** * 做用: * User: duqi * Date: 2017/11/9 * Time: 11:20 */ public class CopyOnWriteArrayListExample { public static void main(String[] args) { CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78}); Iterator<Integer> iterator = numbers.iterator(); numbers.add(100); List<Integer> result = new LinkedList<>(); iterator.forEachRemaining(result::add); assertThat(result).containsOnly(1, 3, 5, 78); Iterator<Integer> iterator2 = numbers.iterator(); numbers.remove(3); List<Integer> result2 = new LinkedList<>(); iterator2.forEachRemaining(result2::add); assertThat(result2).containsOnly(1, 3, 5, 78, 100); } }
例子2:不支持一边遍历一边删除
因为CopyOnWriteArrayList的实现机制——>修改操做和读操做拿到的Iterator对象指向的不是一个数组,所以不支持基于Iterator对象的方法结果的删除:public void remove();
,例子代码以下:oracle
package org.java.learn.concurrent.copyonwritearraylist; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; /** * 做用: User: duqi Date: 2017/11/9 Time: 13:40 */ public class CopyOnWriteArrayListExample2 { public static void main(String[] args) { try { testExceptionThrow(); } catch (Exception e) { e.printStackTrace(); } } private static void testExceptionThrow() { CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78}); Iterator<Integer> integerIterator = numbers.iterator(); while (integerIterator.hasNext()) { integerIterator.remove(); } } }
结论
CopyOnWriteArrayList适合使用在读操做远远大于写操做的场景里,好比缓存。发生修改时候作copy,新老版本分离,保证读的高性能,适用于以读为主的状况。
参考资料
本号专一于后端技术、JVM问题排查和优化、Java面试题、我的成长和自我管理等主题,为读者提供一线开发者的工做和成长经验,期待你能在这里有所收获。
原文出处:https://www.cnblogs.com/javaadu/p/11229161.html