CopyOnWriteArrayList源碼分析

如果要并發(fā)讀寫,如果用synchronized和ReentrantLock那會嚴(yán)重阻塞 讀-讀,讀-寫,寫-寫。如果用ReadWriteLock雖然 讀-讀 ok了,但 讀-寫 還是阻塞,只要有一個線程寫,所有讀線程都會阻塞。

CopyOnWriteArrayList 寫時復(fù)制用空間換時間,讀-讀 和 讀-寫 都不再阻塞。適合多讀偶爾寫,并且允許短暫的讀舊數(shù)據(jù),不要求強一致性。

注意Object[]有volatile,說明Object[]整個數(shù)組引用發(fā)生寫(替換)時,新數(shù)組會對所有線程可見,但Object[]的內(nèi)容發(fā)生寫的時候,是不保證新寫的內(nèi)容對其余線程可見。

volatile對其標(biāo)識的變量或?qū)ο笠糜行В鷮ο髢?nèi)部字段無關(guān)。不要想著把對象標(biāo)識成 volatile 就萬事大吉,認(rèn)為其內(nèi)部變更能對所有線程可見。


讀時直接讀Object[],沒有任何要求。

寫時用lock加鎖互斥其他線程的寫,復(fù)制Object[],在新Object[]里寫入后,把Object[]整個替換回去。Object[]有volatile保證新數(shù)組的引用對之后的讀線程可見。

請注意:lock釋放鎖時也會將釋放鎖之前的所有操作,包括寫volatile的Object[]操作和任意的寫普通共享變量的操作,都刷回主存對其余線程可見。但根據(jù)happen-before規(guī)則要求其余線程要先獲取lock才能完成happen-before鏈,達(dá)到可見性。

但讀操作并沒有獲取lock,不能續(xù)上寫線程unlock后的happen-before鏈,所以不能依靠lock的釋放-獲取達(dá)到可見性,只能通過Object[]自己的volatile達(dá)到可見性。理解這點很重要,因為馬上要分析else里那行看似多余的寫Object[]。


走到else這里Object[]無需寫, 但Object[]寫之前可能會有任意的寫普通共享變量的操作(雖然這里沒有)。剛才說過讀線程沒有獲取lock所以寫線程不能通過unlock來傳遞所有操作的可見性,于是這里冗余的寫volatile的Object[]來傳遞所有操作的可見性,因為讀線程必定讀volatile的Object[]。

從java 1.5開始,JSR-133規(guī)范要求volatile的寫-讀達(dá)到跟synchronized鎖的釋放-獲取 相同的內(nèi)存語義。

volatile寫之前的操作不能重排序到寫之后,volatile寫之前的操作在volatile寫的時刻一起刷回主存,對其余線程可見。volatile讀之后的操作不能重排序到讀之前,volatile讀的時刻清空處理器高速緩存,所有的讀從主存重新讀取,完成可見性。

synchronized釋放鎖的內(nèi)存語義,等同于volatile寫。synchronized獲取鎖的內(nèi)存語義,等同于volatile讀。

其實RenentrantLock的實現(xiàn)就是對AQS內(nèi)的volatile int state字段的讀寫,達(dá)到寫state(釋放lock)之前的所有操作,對別的線程之后的讀state(獲取lock)可見。然后通過cas寫state完成互斥。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容