天天看點

(轉)線程安全的CopyOnWriteArrayList介紹

轉載自:

線程安全的CopyOnWriteArrayList介紹 證明CopyOnWriteArrayList是線程安全的

先寫一段代碼證明CopyOnWriteArrayList确實是線程安全的。

(轉)線程安全的CopyOnWriteArrayList介紹

讀線程

(轉)線程安全的CopyOnWriteArrayList介紹

寫線程

(轉)線程安全的CopyOnWriteArrayList介紹

測試代碼

運作上面的代碼,沒有報出

java.util.ConcurrentModificationException

1

說明了CopyOnWriteArrayList并發多線程的環境下,仍然能很好的工作。

CopyOnWriteArrayList如何做到線程安全的

CopyOnWriteArrayList使用了一種叫

寫時複制

的方法,當有新元素添加到CopyOnWriteArrayList時,先從原有的數組中拷貝一份出來,然後在新的數組做寫操作,寫完之後,再将原來的數組引用指向到新數組。

當有新元素加入的時候,如下圖,建立新數組,并往新數組中加入一個新元素,這個時候,array這個引用仍然是指向原數組的。

(轉)線程安全的CopyOnWriteArrayList介紹

當元素在新數組添加成功後,将array這個引用指向新數組。

(轉)線程安全的CopyOnWriteArrayList介紹

CopyOnWriteArrayList的整個add操作都是在

的保護下進行的。 

這樣做是為了避免在多線程并發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();    }}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

由于所有的寫操作都是在新數組進行的,這個時候如果有線程并發的寫,則通過鎖來控制,如果有線程并發的讀,則分幾種情況: 

1、如果寫操作未完成,那麼直接讀取原數組的資料; 

2、如果寫操作完成,但是引用還未指向新數組,那麼也是讀取原數組資料; 

3、如果寫操作完成,并且引用已經指向了新的數組,那麼直接從新數組中讀取資料。

可見,CopyOnWriteArrayList的

讀操作

是可以不用

加鎖

的。

CopyOnWriteArrayList的使用場景

通過上面的分析,CopyOnWriteArrayList 有幾個缺點: 

1、由于寫操作的時候,需要拷貝數組,會消耗記憶體,如果原數組的内容比較多的情況下,可能導緻young gc或者full gc

2、不能用于

實時讀

的場景,像拷貝數組、新增元素都需要時間,是以調用一個set操作後,讀取到資料可能還是舊的,雖然CopyOnWriteArrayList 能做到

最終一緻性

,但是還是沒法滿足實時性要求;

CopyOnWriteArrayList 合适

讀多寫少

的場景,不過這類慎用 

因為誰也沒法保證CopyOnWriteArrayList 到底要放置多少資料,萬一資料稍微有點多,每次add/set都要重新複制數組,這個代價實在太高昂了。在高性能的網際網路應用中,這種操作分分鐘引起故障。

CopyOnWriteArrayList透露的思想

如上面的分析CopyOnWriteArrayList表達的一些思想: 

1、讀寫分離,讀和寫分開 

2、最終一緻性 

3、使用另外開辟空間的思路,來解決并發沖突

參考的文章 JAVA中的COPYONWRITE容器