參考連接:java
http://blog.csdn.net/hua631150873/article/details/51306021數組
Copy-On-Write簡稱COW,當要修改某個集合時,進行一份集合拷貝,修改新拷貝後的集合,完成後再將新集合指向舊集合。多線程
查看CopyOnWriteArrayList源碼併發
add方法ide
使用final ReentrantLock lock = this.lock;this
lock.lock();.net
在調用add(...)方法前先加鎖,保證同一時間只能有一個線程在添加元素。使用Arrays.copyOf(...)方法複製出另外一個新的數組,並且新的數組的長度比原來多1,拷貝完成後,將新添加的元素賦值給新數組,最後把新的副本數組賦值給舊的數組,而後在finally中釋放鎖。線程
remove方法code
刪除元素,主要判斷是否是刪除最後一個元素,若是是的話,直接複製原來數組的length-1,不然先複製數組index前面到新數組,而後再複製index後面的元素到數組中,最後再把新數組賦值給舊數組的引用。對象
/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). Returns the element that was removed from the list. * * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) // 判斷index是否是最後一個,舉例若是原來數組總共有3個元素,len 是 3, index 是 2, 則numMoved是0; setArray(Arrays.copyOf(elements, len - 1)); else { Object[] newElements = new Object[len - 1]; // 拷貝一個比原來數組長度少一個的數組 System.arraycopy(elements, 0, newElements, 0, index); // 先拷貝index前半部分, System.arraycopy(elements, index + 1, newElements, index, // 再拷貝index+1後面的部分 numMoved); setArray(newElements); // 最後將新數組的引用賦值給舊數組 } return oldValue; } finally { lock.unlock(); } }
其次看System.arraycopy(...)方法的源碼
get方法
set方法
代碼示例:
多線程異常也不是必定會出現的,但使用ArrayList有很大機會會出現併發異常,下面改用CopyOnWriteArrayList能夠解決這個問題。若是使用ArrayList沒有出現併發異常,建議把線程池改大點。
package com.jerry.entity; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CopyOnWriteListTest { private static final int THREAD_POOL_MAX_NUM = 10; // private List<String> mList = new ArrayList<String>(); private List<String> mList = new CopyOnWriteArrayList<String>(); public static void main(String[] args) { new CopyOnWriteListTest().start(); } private void initData() { this.mList.add("code_99"); this.mList.add("code_98"); this.mList.add("code_97"); } private void start() { initData(); ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_MAX_NUM); for(int i=1; i<=THREAD_POOL_MAX_NUM; i++) { executorService.execute(new ListReader(this.mList)); String codeId = "code_"+i; executorService.execute(new ListWriter(this.mList, codeId)); } executorService.shutdown(); } private class ListReader implements Runnable { private List<String> rList; public ListReader(List<String> list) { this.rList = list; } @Override public void run() { if(this.rList != null) { System.out.println("Reader Thread: "+Thread.currentThread().getName()+" --> "+this.rList.toString()); } } } private class ListWriter implements Runnable { private List<String> wList; private String codeId; public ListWriter(List<String> list, String codeId) { this.wList = list; this.codeId = codeId; } @Override public void run() { if(this.wList != null) { this.wList.add(codeId); System.out.println("Writer Thread: "+Thread.currentThread().getName()+" --> "+this.wList.toString()); } } } }
CopyOnWriteArrayList的優勢和缺點:
優勢:解決多線程的併發問題
缺點:
1.內存佔用有問題:寫時拷貝,內存中會存在兩份對象。很明顯兩個數組同時駐紮在內存中,若是實際應用中,數據比較多,佔用內存會比較大,針對這個能夠用ConcurrentHashMap來代替。所以,不建議對大對象使用CopyOnWriteArrayList。
ConcurrentHashMap: http://www.javashuo.com/article/p-fzttzvam-gb.html
2. 數據的一致性:CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性。所以對實時性要求的數據,不建議使用COW。