天天看點

Java開發之高并發程式設計篇(八)——安全通路的集合(2)

作者:彙智動力IT學院

CopyOnWriteArrayList和CopyOnWriteArrayList

因為CopyOnWriteArraySet底層也是使用了CopyOnWriteArrayList來實作的,是以本篇文章以CopyOnWriteArrayList講解為主。

CopyOnWriteArrayList是JDK1.5之後提供的一個List接口的實作子類,位于java.util.concurrent并發包下,它是一個線程安全的變體的實作。其實CopyOnWriteArrayList在我們的一些開發場景下就有使用,隻不過我們自己很少使用它而已,例如:我們JDBC操作資料庫的時候注冊的Driver驅動資訊就是存放在CopyOnWriteArrayList裡來保證多線程安全的。

Java開發之高并發程式設計篇(八)——安全通路的集合(2)

CopyOnWriteArrayList重點突出的是CopyOnWrite(寫入時複制),即當有Write(add、set、remove等)操作的時候進行數組的Copy複制新的數組副本來實作,是一種讀寫分離的并發政策。檢視源碼分析其原理:

Java開發之高并發程式設計篇(八)——安全通路的集合(2)

首先看到的是CopyOnWriteArrayList裡面的資料都是存放在一個被volatile修飾的數組中,即底層是動态數組的實作,隻不過這個數組可以保證線程操作的可見性和代碼的有序性執行。并且這裡看到有個ReentrantLock是以我們猜測底層可能使用了Lock鎖的實作。再來看下其write操作代碼:

Java開發之高并發程式設計篇(八)——安全通路的集合(2)
Java開發之高并發程式設計篇(八)——安全通路的集合(2)
Java開發之高并發程式設計篇(八)——安全通路的集合(2)

我們發現确實使用了不被序列化的ReentrantLock鎖來實作的線程同步,并且鎖的粒度大幾乎囊括了整個操作方法,這裡沒有考慮使用synchronized的原因是因為lock鎖操作起來更加靈活,我們可以看到它在try...finally結構中不管是否操作成功都會釋放鎖不會導緻鎖的阻塞。

CopyOnWriteArrayList設計的好處:

1.線程安全操作和效率

底層使用了ReentrantLock保證了增删改write操作的線程安全性,并且相比于Vector每個操作方法都通過synchronized同步方法處理來說,CopyOnWriteArrayList的同步鎖更加靈活,并且CopyOnWriteArrayList讀操作是沒有鎖的,是以讀操作效率要高于Vector。

2.讀寫分離

CopyOnWriteArrayList其周遊操作和寫操作是作用再不同的數組中,周遊是使用了其聲明的Object[] array進行周遊(其疊代器也不支援write操作),而寫的時候是新建立的數組操作是以不會出現線程安全的問題。這點List則不行,List在周遊時,如果中途有别的線程對list資料進行修改,則會抛出ConcurrentModificationException異常,如圖所示:

Java開發之高并發程式設計篇(八)——安全通路的集合(2)
Java開發之高并發程式設計篇(八)——安全通路的集合(2)

使用CopyOnWriteArrayList則程式不會報錯。

3.資料最終一緻性

雖然CopyOnWriteArrayList讀和寫使用的并不是同一個array對象,不能夠保證一個線程資料在寫的時候另外一個線程立馬能讀到,但是因為使用了鎖是以最終讀到的資料都是一緻的。

CopyOnWriteArrayList的缺點:

1.記憶體占用太大,缺點很明顯的就是在wirte操作的時候,我們發現它都是使用Arrays.copyOf複制到一個新數組來完成的,如果有頻繁的wirte操作就比較的占用記憶體,可能引起頻繁的JVMYong GC和Full GC操作,write操作效率比較低;

2.無法保證資料的實時一緻性,因為其讀和寫在不同的array中操作,是以如果多個線程在操作,例如線程A、B,如果想要保證線程A寫入線程B馬上能讀到就不行,但是不适合對實時性要求比較高的場景。

CopyOnWriteArrayList使用場景:

CopyOnWriteArrayList适合使用在資料讀多寫少的情況下,如果資料對實時性要求比較高的業務場景則不适合使用CopyOnWriteArrayList。例如:如果業務需要配置資訊、有黑名單、白名單等一些更新不頻繁但是讀取頻繁的場景就适合使用CopyOnWriteArrayList在存儲,另外很多的一些中間件之類的核心結構也是使用的CopyOnWriteArrayList比如:MQ中間件的一種kafaka的核心資料結構等。

繼續閱讀