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(); } }