河北省建設(shè)執(zhí)業(yè)資格中心網(wǎng)站網(wǎng)絡(luò)營銷推廣技巧
一、CopyOnWriteArrayList 源碼解讀
在 JUC
中,對于 ArrayList
的線程安全用法,比較推崇于使用 CopyOnWriteArrayList
,那 CopyOnWriteArrayList
是怎么解決線程安全問題的呢,本文帶領(lǐng)大家一起解讀下 CopyOnWriteArrayList
的源碼,主要對幾個常用的函數(shù)進行講解。
在進行 CopyOnWriteArrayList
的源碼講解之前,先看下同樣實現(xiàn)了線程安全的 Vector
,很多文章都說不推薦使用 Vector
,其主要原因是性能太差了,那性能為什么這么差呢?可以看下 Vector
add
和 get
的源碼:
Vector
的添加和讀取操作都被加上了 synchronized
鎖,當并發(fā)情況下,因為鎖的存在相當于變成了單線程的操作,所以效率肯定低,同樣這樣的優(yōu)點就是保證了數(shù)據(jù)的唯一性,不會讀取到臟數(shù)據(jù)。
下面再看下 CopyOnWriteArrayList
是如何解決并發(fā)問題的呢。
首先看下 CopyOnWriteArrayList
的全局變量有哪些:
其中 lock
鎖就是每次在做寫操作時,鎖的句柄,array
就是具體存儲數(shù)據(jù)的數(shù)組,注意這里的 array
被 volatile
所修飾,因此可以在并發(fā)情況下實現(xiàn)數(shù)據(jù)的可見性。
當 new
創(chuàng)建了一個 CopyOnWriteArrayList
時,如果是使用無參的構(gòu)造函數(shù),則將 array
的長度默認成 0
,創(chuàng)建了一個空的數(shù)組。
在使用 add
添加數(shù)據(jù)時,先使用 lock
上鎖,并獲取到當前的 array
數(shù)組,然后對 array
進行 copyOf
,新的數(shù)組的長度是之前的長度 +1
,這樣才能存放當前新的值,將新值填充后,再替換掉舊的 array
數(shù)組后,釋放當前鎖。
在使用 get
獲取指定下邊數(shù)據(jù)時,直接對當前的 array
進行操作:
在進行 remove
刪除時,先使用 lock
上鎖,然后再獲取當前的 array
數(shù)組,如果傳入的 index
正好是最后一個,那么 numMoved
計算出來就是 0
,則使用 copyOf
,長度進行 -1
去除最后一個數(shù)據(jù)。否則傳入的不是最后一個,先聲明一個新的 array
數(shù)組,數(shù)組的長度就是舊的 array
的 len - 1
,再將 0
到 index
的數(shù)據(jù) arraycopy
至新的 array
數(shù)組,然后再將 index + 1
后的再 arraycopy
至新的 array
數(shù)組,最后將新的 array
數(shù)組替換舊的,然后釋放鎖。
二、總結(jié)
- 當
new
新建一個CopyOnWriteArrayList
后會生成一個數(shù)組array
來存放添加的內(nèi)容,如果是無參的構(gòu)造函數(shù),則array
的長度為0
,添加數(shù)據(jù)時再進行擴容。同時會聲明一個ReentrantLock
鎖。 - 當進行
add
操作時,先進行上鎖,然后對當前的array
進行copyOf
,并且新的長度是之前的長度+1
,這樣才能存放當前新的值,將新值填充后,再替換掉舊的array
數(shù)組后,釋放當前鎖。 - 當使用
get
獲取數(shù)據(jù)時,無需上鎖,直接讀取當前array
數(shù)組的指定位置。 - 當使用
remove
時,同樣先進行上鎖,然后再獲取當前的array
數(shù)組,如果傳入的index
正好是最后一個,則使用copyOf
,長度進行-1
,否則的話先聲明一個新的array
數(shù)組,現(xiàn)將0
到index
的數(shù)據(jù)arraycopy
至新的array
數(shù)組,然后再將index + 1
后的再arraycopy
至新的array
數(shù)組,最后將新的array
數(shù)組替換舊的,然后釋放鎖。
讀下來之后可以感覺出來 CopyOnWriteArrayList
的源碼非常容易理解和閱讀,同時我們也可以看出一些問題,CopyOnWriteArrayList
實現(xiàn)了寫寫隔離,但讀讀是可以共享的,這就有可能出現(xiàn)當某個數(shù)據(jù)再修改時,讀進行了操作,導致讀取到的還是舊的數(shù)據(jù)。還有就是每次寫操作都對數(shù)組進行 Copy
,假如數(shù)據(jù)量非常大的情況下,進行 Copy
消耗的資源則會進行 x 2
,因此使用 CopyOnWriteArrayList
時,需要考慮下自己的數(shù)據(jù)量以及讀寫的頻次。