CopyOnWriteArrayList原始碼簡析

2021-10-05 15:23:22 字數 2174 閱讀 3974

copyonwritearraylist,是在concurrent包下的乙個類,說明這是乙個併發安全的類,從命名來看,這是乙個執行緒安全的arraylist,copyonwrite則是實現他併發安全的機制,即寫時複製

copyonwritearraylist類的用法和arraylist基本是一樣的,所以就不寫demo了,這裡直接來看一下add方法的原始碼吧

public

boolean

add(e e)

finally

}

這裡實現併發安全的方法很簡單,就是加了乙個reentrantlock,我們知道list的底層其實就是陣列,這裡在操作list的時候,每次都是建立了乙個新的陣列,將舊陣列的值複製到新陣列,然後再將要新增的元素加入新陣列中

其實copyonwritearraylist類中的增刪改都是這個套路,先加鎖,然後再使用舊陣列複製出來乙個新陣列,進行各種的增刪改操作

看到這裡,相信大家應該有點理解寫時複製機制的意思了,無非就是在進行寫操作的時候,複製乙個新的陣列出來,將寫操作之後的結果放入到新陣列之後然後返回,那為什麼要這麼折騰呢?

結果就隱藏在讀操作中,下面再來看看get方法的原始碼

public e get

(int index)

private e get

(object[

] a,

int index)

get方法的原始碼其實就是最簡單的從陣列中獲取我們想要位置的元素,沒有任何加鎖,或是cas等操作,那自然效率很高了,get方法能夠什麼都不做還能保證併發安全性就要歸功於寫時複製機制了

private

transient

volatile object[

] array;

這裡補充一下copyonwritearraylist操作的陣列的變數,array是使用了volatile關鍵字修飾的,所以他可以保證可見性

好,現在我們假設乙個add操作和get操作併發來執行了,這裡會存在兩種情況

第一種:add操作對新陣列的操作還沒有完成,此時copyonwritearraylist中的陣列還是老陣列,此時讀操作就基於老陣列完成

第二種:add操作執行完成,同時copyonwritearraylist的新陣列也設定完畢了,volatile關鍵字能保證陣列的可見性,立馬就能被讀操作的執行緒訪問到

這兩種情況下,寫操作和讀操作是不會發生併發問題的,因為寫操作一直是基於陣列副本來增刪改,讀操作則一直是讀的陣列的array,這就是為什麼要寫時複製了,這種做法是典型的空間換時間

通過上面的分析,我們也可以知道copyonwritearraylist的使用場景了,寫操作的時候,需要加鎖,同時複製陣列副本進行操作,而在讀操作的時候,不需要做其他的併發安全處理,所以copyonwritearraylist非常適合讀多寫少的場景

既然copyonwritearraylist是併發安全的,那麼是不是我們所做的所有操作都可以保證併發安全性呢?

在上面我們說讀操作的時候,可能會出現兩種情況,一種情況是讀舊陣列,一種是讀新陣列,所以在一小段時間內,不同的執行緒讀操作可能會讀到不同的值,產生弱一致性的問題

還有另外乙個場景,在copyonwritearraylist迭代的時候,這裡貼出一部分原始碼

public iterator

iterator()

static

final

class

cowiterator

implements

listiterator

//省略原始碼

}

從原始碼中可以看出在迭代的時候,new了乙個cowiterator物件,裡面的snapshot快照使用的當前的陣列,之後的迭代操作都是基於這個snapshot來做的,也就是說如果此時有別的執行緒來進行寫操作的話,iterator方法裡面使用的還是舊陣列,所以不會產生併發安全問題,但是迭代出的資料也不一定就是最新的資料,如果你非要保證資料的強一致性的話,還是需要通過加鎖來保證

copyonwritearraylist還有乙個問題,之前提到了一下,就是空間換時間,如果你要操作的list非常大的話,多個執行緒運算元組副本的話,可能會消耗大量的記憶體空間,這也是需要注意的乙個點

在使用集合包下面的併發工具類的時候,一定要做到對各個類的特性心中有數,這樣才能根據不同的業務場景選擇我們所需要的工具,做到游刃有餘

Sample CelShading原始碼簡析

ifndef celshading h define celshading h include sdksample.h using namespace ogre using namespace ogrebites class ogresampleclas port sample celshading...

MyBatis原始碼簡讀 原始碼拆分

整個mybatis原始碼大概被分為三個部分 目前基礎類 的範圍是 註解類 annotations包 繫結模組類 binding包 配置解析 builder包 快取 cache包 資料來源 datasource包 異常 exceptions包 jdbc jdbc包 日誌 logging包 io io包...

Sample BSP原始碼簡析

ifndef bsp h define bsp h include sdksample.h include filesystemlayer.h filesystemlayer.h 用來處理檔案系統的目錄 路徑等資訊 後面的mfslayer getconfigfilepath就是用了該檔案中定義的類。...