Java 中的 CopyOnWriteArrayList

本篇文章是【Java集合系列】文章的第三篇侦啸,本系列將會(huì)逐個(gè)分析 Java 中的常用集合的特性及實(shí)現(xiàn)昭灵,然后對(duì)比不同場(chǎng)景下應(yīng)該選擇哪種集合使用偎痛。

List 系列

CopyOnWriteArrayList

先看看百科上關(guān)于 COW 的介紹

寫入時(shí)復(fù)制(英語:Copy-on-write衅鹿,簡(jiǎn)稱COW)是一種計(jì)算機(jī)程序設(shè)計(jì)領(lǐng)域的優(yōu)化策略愕够。其核心思想是,如果有多個(gè)調(diào)用者(callers)同時(shí)請(qǐng)求相同資源(如內(nèi)存或磁盤上的數(shù)據(jù)存儲(chǔ))浪讳,他們會(huì)共同獲取相同的指針指向相同的資源缰盏,直到某個(gè)調(diào)用者試圖修改資源的內(nèi)容時(shí),系統(tǒng)才會(huì)真正復(fù)制一份專用副本(private copy)給該調(diào)用者,而其他調(diào)用者所見到的最初的資源仍然保持不變口猜。這過程對(duì)其他的調(diào)用者都是透明的(transparently)负溪。此作法主要的優(yōu)點(diǎn)是如果調(diào)用者沒有修改該資源,就不會(huì)有副本(private copy)被創(chuàng)建济炎,因此多個(gè)調(diào)用者只是讀取操作時(shí)可以共享同一份資源川抡。

簡(jiǎn)單來說,就是讀取時(shí)直接讀取不用加鎖同步须尚,寫入數(shù)據(jù)時(shí)會(huì)復(fù)制一份副本崖堤,然后將新的數(shù)據(jù)寫入到副本中,然后再把副本替換成原來的數(shù)據(jù)耐床。

因此 CopyOnWriteArrayList 是線程安全的密幔,另外也允許 null 元素。

這種方式導(dǎo)致了讀取速度很快撩轰,寫入速度較慢胯甩,適合多線程環(huán)境中經(jīng)常讀取但寫入很少的場(chǎng)景。

所以如果有個(gè)場(chǎng)景需要在多線程環(huán)境中使用堪嫂,頻繁讀取缸棵,但寫入次頻率很低资柔,例如黑名單白名單丛晌,每天更新一次签餐,那就可以使用 CopyOnWriteArrayList 來實(shí)現(xiàn)。

其中的迭代器是不允許對(duì)數(shù)據(jù)修改的愚战,調(diào)用remove,set,add這些方法會(huì)直接拋異常娇唯。

一般可以按照常規(guī)的 List 來使用,其中沒有什么特殊的方法寂玲,就直接開始看源碼吧塔插。

CopyOnWriteArrayList 下面簡(jiǎn)稱 COWList。

源碼

我們先看其中兩個(gè)最重要的方法:

private transient volatile Object[] array;
final Object[] getArray() {
    return array;
}
final void setArray(Object[] a) {
    array = a;
}

上面定義的array變量就是 COWList 中存儲(chǔ)數(shù)據(jù)的地方拓哟。
所有的讀取寫入操作都是通過對(duì)這個(gè)數(shù)組的操作想许。

因此構(gòu)造函數(shù)也是創(chuàng)建一個(gè)空的數(shù)組然后調(diào)用setArray方法設(shè)置數(shù)組。

public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}

COWList 的優(yōu)勢(shì)之一就是即使在多線程環(huán)境中断序,讀取也是不需要加鎖的流纹,所以效率很高,那么我們來看看讀取方法是如何實(shí)現(xiàn)的:

public E get(int index) {
    return get(getArray(), index);
}
private E get(Object[] a, int index) {
    return (E) a[index];
}

可以看到這里直接通過索引讀取了數(shù)組的值违诗,沒有加鎖漱凝。這根我們使用Collections.synchronizedList方法獲取到的SynchronizedList效率要高。

可以看下SynchronizedList是如何實(shí)現(xiàn)讀取及寫入的:

//SynchronizedList
public E get(int index) {
    synchronized (mutex) {
        return list.get(index);
    }
}
public E set(int index, E element) {
    synchronized (mutex) {
        return list.set(index, element);
    }
}

對(duì)比一下顯而易見诸迟,讀取不加鎖的話效率就高多了茸炒。

那么 COWList 是如何實(shí)現(xiàn)多線程環(huán)境下的安全問題的呢愕乎,看下寫入方法就明白了。

public boolean add(E e) {
    synchronized (lock) {
        Object[] elements = getArray();
        int len = elements.length;
        //復(fù)制數(shù)組
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    }
}

可以看到添加元素會(huì)加鎖壁公,加鎖之后會(huì)將原數(shù)組復(fù)制到一個(gè)更大(len + 1)的數(shù)組中去感论,并將追加元素添加到數(shù)組末尾,完成操作后調(diào)用setArray方法設(shè)置新數(shù)組紊册。

所以寫入的效率是很低的笛粘,需要進(jìn)行一次數(shù)組復(fù)制操作,用 COWList 時(shí)應(yīng)該盡量降低寫入操作的頻率湿硝,如果需要經(jīng)常寫入,那說明可能不適合用這個(gè)润努。

歡迎關(guān)注我的公眾號(hào)关斜,還有更多干貨。
微信掃描二維碼或者搜索:zhangke_blog

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末铺浇,一起剝皮案震驚了整個(gè)濱河市痢畜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鳍侣,老刑警劉巖丁稀,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異倚聚,居然都是意外死亡线衫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門惑折,熙熙樓的掌柜王于貴愁眉苦臉地迎上來授账,“玉大人,你說我怎么就攤上這事惨驶“兹龋” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵粗卜,是天一觀的道長(zhǎng)屋确。 經(jīng)常有香客問我,道長(zhǎng)续扔,這世上最難降的妖魔是什么攻臀? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮纱昧,結(jié)果婚禮上茵烈,老公的妹妹穿的比我還像新娘。我一直安慰自己砌些,他們只是感情好呜投,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布加匈。 她就那樣靜靜地躺著,像睡著了一般仑荐。 火紅的嫁衣襯著肌膚如雪雕拼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天粘招,我揣著相機(jī)與錄音啥寇,去河邊找鬼。 笑死洒扎,一個(gè)胖子當(dāng)著我的面吹牛辑甜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播袍冷,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼磷醋,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了胡诗?” 一聲冷哼從身側(cè)響起邓线,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎煌恢,沒想到半個(gè)月后骇陈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瑰抵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年你雌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片二汛。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匪蝙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出习贫,到底是詐尸還是另有隱情逛球,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布苫昌,位于F島的核電站颤绕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏祟身。R本人自食惡果不足惜奥务,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望袜硫。 院中可真熱鬧氯葬,春花似錦、人聲如沸婉陷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至闯睹,卻和暖如春戏羽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背楼吃。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工始花, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人孩锡。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓酷宵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親躬窜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浇垦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355