CopyOnWrite是一种线程安全的容器, 适用于读多写少的场景, 从字面的理解就能够知道这是一个底层复制的机制,也就是在不影响读的状况下,会采用将原有的数据copy出来,在此基础上进行更新的操做,因此保证了线程的安全,固然,若是写操做很是频繁,不建议读者使用此容器. CopyOnWrite容器多用于并发且读操做频繁的场景.java
CopyOnWrite容器即写时复制的容器。通俗的理解是当咱们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,而后新的容器里添加元素,添加完元素以后,再将原容器的引用指向新的容器。这样作的好处是咱们能够对CopyOnWrite容器进行并发的读,而不须要加锁,由于当前容器不会添加任何元素。因此CopyOnWrite容器也是一种读写分离的思想,读和写不一样的容器。安全
下面来看个例子理解下为何在多并发场景下须要CopyOnWrite容器并发
public class CopyOnWriteList { static class ReadTask implements Runnable { private List<String> list = null; public ReadTask(List<String> list) { this.list = list; } @Override public void run() { for (String l : list) { System.out.println(l); } } } static class WriteTask implements Runnable { private List<String> list = null; int index = -1; public WriteTask(List<String> list, int index) { this.list = list; this.index = index; } @Override public void run() { list.remove(index); list.add(index, "add_" + index); } } public static void main(String[] args) { List<String> list = new ArrayList<String>(); //List<String> list = new CopyOnWriteArrayList<String>(); int num = 10; for (int i = 0; i < num; i++) { list.add(i, "init_" + i); } ExecutorService executor = Executors.newFixedThreadPool(num); for (int i = 0; i < num; i++) { executor.execute(new ReadTask(list)); executor.execute(new WriteTask(list, i)); } executor.shutdown(); } }
上面的是一个在并发环境下的读写List的例子,读者运行过这个代码后就会发现,在并发状况下,对容器的读写会抛异常.ide
add_3Exception in thread "pool-1-thread-3" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at java.util.ArrayList$Itr.next(Unknown Source) at com.suning.jdk.CopyOnWriteList$ReadTask.run(CopyOnWriteList.java:19) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source)
感兴趣的读者能够将注释掉的CopyOnWriteArrayList恢复,而后看下运行结果, 在并发状况下,CopyOnWriteArrayList会游刃有余.this
下面咱们看下源码,究竟CopyOnWriteArrayList内部是怎么作到的线程
public boolean add(E paramE) { ReentrantLock localReentrantLock = this.lock; localReentrantLock.lock(); try { Object[] arrayOfObject1 = getArray(); int i = arrayOfObject1.length; Object[] arrayOfObject2 = Arrays.copyOf(arrayOfObject1, i + 1); arrayOfObject2[i] = paramE; setArray(arrayOfObject2); boolean bool = true; return bool; } finally { localReentrantLock.unlock(); } }