轉載自:線程安全的CopyOnWriteArrayList介紹
證明CopyOnWriteArrayList是線程安全的
先寫一段代碼證明CopyOnWriteArrayList確實是線程安全的。



運行上面的代碼,沒有報出
java.util.ConcurrentModificationException
1
說明了CopyOnWriteArrayList并發(fā)多線程的環(huán)境下,仍然能很好的工作。
CopyOnWriteArrayList如何做到線程安全的
CopyOnWriteArrayList使用了一種叫寫時復制的方法,當有新元素添加到CopyOnWriteArrayList時,先從原有的數組中拷貝一份出來,然后在新的數組做寫操作,寫完之后,再將原來的數組引用指向到新數組。
當有新元素加入的時候,如下圖,創(chuàng)建新數組,并往新數組中加入一個新元素,這個時候,array這個引用仍然是指向原數組的。
當元素在新數組添加成功后,將array這個引用指向新數組。
CopyOnWriteArrayList的整個add操作都是在鎖的保護下進行的。?
這樣做是為了避免在多線程并發(fā)add的時候,復制出多個副本出來,把數據搞亂了,導致最終的數組數據不是我們期望的。
CopyOnWriteArrayList的add操作的源代碼如下:
publicbooleanadd(E e) {//1、先加鎖final ReentrantLocklock=this.lock;lock.lock();try{? ? ? ? Object[] elements = getArray();intlen = elements.length;//2、拷貝數組Object[] newElements = Arrays.copyOf(elements, len +1);//3、將元素加入到新數組中newElements[len] = e;//4、將array引用指向到新數組setArray(newElements);returntrue;? ? }finally{//5、解鎖lock.unlock();? ? }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
由于所有的寫操作都是在新數組進行的,這個時候如果有線程并發(fā)的寫,則通過鎖來控制,如果有線程并發(fā)的讀,則分幾種情況:?
1、如果寫操作未完成,那么直接讀取原數組的數據;?
2、如果寫操作完成,但是引用還未指向新數組,那么也是讀取原數組數據;?
3、如果寫操作完成,并且引用已經指向了新的數組,那么直接從新數組中讀取數據。
可見,CopyOnWriteArrayList的讀操作是可以不用加鎖的。
CopyOnWriteArrayList的使用場景
通過上面的分析,CopyOnWriteArrayList?有幾個缺點:?
1、由于寫操作的時候,需要拷貝數組,會消耗內存,如果原數組的內容比較多的情況下,可能導致young gc或者full gc
2、不能用于實時讀的場景,像拷貝數組、新增元素都需要時間,所以調用一個set操作后,讀取到數據可能還是舊的,雖然CopyOnWriteArrayList?能做到最終一致性,但是還是沒法滿足實時性要求;
CopyOnWriteArrayList?合適讀多寫少的場景,不過這類慎用?
因為誰也沒法保證CopyOnWriteArrayList?到底要放置多少數據,萬一數據稍微有點多,每次add/set都要重新復制數組,這個代價實在太高昂了。在高性能的互聯(lián)網應用中,這種操作分分鐘引起故障。
CopyOnWriteArrayList透露的思想
如上面的分析CopyOnWriteArrayList表達的一些思想:?
1、讀寫分離,讀和寫分開?
2、最終一致性?
3、使用另外開辟空間的思路,來解決并發(fā)沖突
參考的文章