CopyOnWriteArrayList原理解析

2021-10-06 08:50:29 字數 3550 閱讀 4439

copyonwritearraylist是乙個執行緒安全的arraylist,對其進行的修改操作都是在底層的乙個複製的陣列(快照)上進行的,也就是使用了寫時複製策略。如圖所示是copyonwritearraylist的類圖結構:

上圖有個小瑕疵,lock 是 包級私有,而不是 protected。

能夠看到,每個copyonwritearraylist物件都有乙個array陣列用來存放具體元素,而reentrantlock則用來保證只有乙個執行緒對array進行修改。reentrantlock本身是乙個獨佔鎖,同時只有乙個執行緒能夠獲取。接下來看一下其中的一些方法**。

共有三個建構函式:

public

copyonwritearraylist()

public

copyonwritearraylist

(e tocopyin)

//將傳入引數集合中的元素複製到本list中

public

copyonwritearraylist

(collection<

?extends

e> c)

setarray

(elements)

;}

setarray方法很簡單:

final

void

setarray

(object[

] a)

新增元素有很多方法,包括add(e e), add(int index, e element)等,原理基本上相同,所以我們只看add(e e)的原始碼。

public

boolean

add(e e)

finally

}

**很簡單,就是將原來的元素複製到了乙個新陣列中,且長度應該加1,然後在新陣列末尾加上要新增的元素,最後設定新陣列為自己的array。

使用e get(int index)方法獲取下標為index的元素:

public e get

(int index)

final object[

]getarray()

private e get

(object[

] a,

int index)

這個方法是執行緒不安全的,因為這個分成了兩步,分別是獲取陣列和獲取元素,而且中間過程沒有加鎖。假設當前執行緒在獲取陣列(執行getarray())後,其他執行緒修改了這個copyonwritearraylist,那麼它裡面的元素就會改變,但此時當前執行緒返回的仍然是舊的陣列,所以返回的元素就不是最新的了,這就是寫時複製策略產生的弱一致性問題

使用e set (int index, e element)修改list中指定元素的值,**如下:

public e set

(int index, e element)

else

return oldvalue;

}finally

}

使用public e remove(int index)方法,**如下:

public e remove

(int index)

return oldvalue;

}finally

}

也很簡單,就是將元素分兩次複製到新陣列中,然後設定array為新陣列。返回的是刪除的元素。

我們先看一下迭代器是怎麼使用的:

public

static

void

main

(string[

] args)

很簡單,那弱一致性是怎麼回事呢,它是指返回迭代器後,其他執行緒對list的增刪改對迭代器是不可見的。接下來看一下為什麼會這樣:

public iterator

iterator()

static

final

class

cowiterator

implements

listiterator

public

boolean

hasnext()

@suppresswarnings

("unchecked"

)public e next()

在呼叫iterator()方法後,會返回乙個cowiterator物件,cowiterator物件的snapshot變數儲存了當前list的內容,cursor是遍歷list時資料的下標。

那麼為什麼說snapshot是list的快找呢,明明傳的是引用。其實這就和copyonwritearraylist本身有關了,如果在返回迭代器後沒有對裡面的陣列array進行修改,則這兩個變數指向的確實是同乙個陣列;但是若修改了,則根據前面所講,它是會新建乙個陣列,然後將修改後的陣列複製到新建的陣列,而老的陣列就會被「丟棄」,所以如果修改了陣列,則此時snapshot指向的還是原來的陣列,而array變數已經指向了新的修改後的陣列了。這也就說明獲取迭代器後,使用迭代器元素時,其他執行緒對該list的增刪改不可見,因為他們操作的是兩個不同的陣列,這就是弱一致性

接下來就演示一下這個現象:

public

class

copylist})

; iterator

itr = arraylist.

iterator()

; threadone.

start()

; threadone.

join()

;while

(itr.

hasnext()

) system.out.

println

(itr.

next()

);}}

執行結果如下,說明雖然執行緒threadone改變了這個list,但是獲取了迭代器後,它指向的還是舊的陣列,所以遍歷的時候還是舊的陣列內容。所以==獲取迭代器的操作必須在子執行緒操作之前進行。

hello

alibaba

welcome

tohangzhou

copyonwritearraylist使用寫時複製策略保證list的一致性,而獲取–修改–寫入三個步驟不是原子性,所以需要乙個獨佔鎖保證修改資料時只有乙個執行緒能夠進行。另外,copyonwritearraylist提供了弱一致性的迭代器,從而保證在獲取迭代器後,其他執行緒對list的修改是不可見的,迭代器遍歷的陣列是乙個快照。

CopyOnWriteArrayList原始碼解讀

概述copyonwritearraylist是jdk concurrent包中提供的乙個非阻塞型的,執行緒安全的list實現。copyonwritearraylist在進行資料修改時,都不會對資料進行鎖定,每次修改時,先拷貝整個陣列,然後修改其中的一些元素,完成上述操作後,替換整個陣列的指標。對co...

CopyOnWriteArrayList 原始碼分析

1.copyonwritearraylist 是執行緒安全的arraylist,適用於儲存的資料量不大,讀操作遠多於寫操作,對實時性要求不高的場景。copyonwritearraylist 對 讀操作不同步,對寫操作同步。在進行寫操作時,會對共享變數進行copy,在副本上進行更新,然後將更新好的副本...

CopyOnWriteArrayList原始碼分析

原始碼基於1.8.0 112 copyonwritearraylist也是通過陣列來儲存元素,閱讀過之前的arraylist的話這邊應該很容易理解 原理 copyonwritearraylist內部通過陣列來儲存資料,每次修改list都會產生乙個新的陣列,然後複製原始資料。修改方法都通過內部的而乙個...