juc系列-CopyOnWriteArrayList

CopyOnWriteArrayList是一個ArrayList線程安全的變體源祈。當數(shù)組內(nèi)容有所變化時吧趣,拷貝一份新的出來任内,在新對象上進行修改操作赞哗,完成后把新對象引用賦值給array屬性。每發(fā)生一次改變,就需要復制一份數(shù)據(jù)万伤,這樣復制是需要一定開銷的窒悔,所以CopyOnWriteArrayList適合讀操作遠大于修改操作的情況中。

CopyOnWriteArrayList構(gòu)造函數(shù):

public CopyOnWriteArrayList()

//Collection做初始化參數(shù)
public CopyOnWriteArrayList(Collection<? extends E> c)

//Array做初始化參數(shù)
public CopyOnWriteArrayList(E[] toCopyIn)

1.讀操作

privateE get(Object[] a, intindex) {
    return(E) a[index];
}
 
publicE get(intindex) {
    returnget(getArray(), index);
}

直接讀取壕翩,不需要加鎖,因為即使讀取過程中有其他線程改動List傅寡,也是開辟新的數(shù)組并在新數(shù)組上改動放妈,舊數(shù)組對象始終是可用的。

2.寫操作
在CopyOnWriteArrayList中寫操作過程大致是這樣的荐操。在原有數(shù)組的基礎(chǔ)上拷貝一份新的數(shù)組(容器副本)芜抒,將改動操作在新數(shù)組上完成(即把新增元素加入新數(shù)組中),然后再把新數(shù)組對象的引用賦給CopyOnWriteArrayList的array托启。顯然宅倒,在多線程環(huán)境中,為了保證線程安全屯耸,整個過程需要加鎖拐迁。所以CopyOnWriteArrayList的寫操作性能損耗是很大的,一方面需要競爭獲取鎖疗绣,另一方面需要進行復制操作线召。

下面以add(int index, E element)方法為例說明CopyOnWriteArrayList的修改操作:

//指定位置增加元素
    public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        //修改array前獲取鎖
        lock.lock();
        try {
        //獲取原有array
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
            //index=length,即數(shù)組尾部新增一個元素,同add(E element)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
            //兩次復制
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            //新增元素
            newElements[index] = element;
            //將新數(shù)組引用賦值給array
            setArray(newElements);
        } finally {
        //釋放鎖
            lock.unlock();
        }
    }

除了add方法,還有remove多矮、removeRange缓淹、addIfAbsent等其他修改操作原理都是一樣的,都是新new一個數(shù)組對象塔逃,在新array上進行修改操作讯壶,完事后再將新數(shù)組引用賦值給實例變量array,當然修改操作都是需要加鎖的湾盗。

通過Iterator迭代器遍歷CopyOnWriteArrayList

通過Iterator遍歷CopyOnWriteArrayList的時候伏蚊,不允許對array進行修改。remove格粪、add丙挽、set方法直接拋出UnsupportedOperationException異常,這點是和普通list不同的地方匀借。

線程安全性

我們可以看到颜阐,CopyOnWriteArrayList內(nèi)部的array數(shù)組對象從被創(chuàng)建,到這個對象生命結(jié)束吓肋,是不可變的凳怨。變的是array變量的引用值,每做一次修改操作,array變量就指向新生成的數(shù)組對象肤舞,原對象被gc紫新,如此反復。這種方式核心思想是減少鎖競爭李剖,從而提高高并發(fā)時的讀取性能芒率,但一定程度上犧牲了寫的性能。
由此可得:“寫入時復制(Copy-On-Write)”容器的線程安全性在于:只有正確地發(fā)布一個事實不可變的對象篙顺,那么在訪問該對象時就不需要做同步操作偶芍。這也就解釋了為什么通過迭代器Iterator是不允許進行修改操作的了。

timestamp_1470147855946_test.png
優(yōu)點

讀操作無需加鎖,并發(fā)環(huán)境性能不錯德玫,但只適用于讀操作遠大于寫操作的場景匪蟀。

缺陷
  1. 缺少同步控制,數(shù)據(jù)的一致性沒法保證宰僧。在并發(fā)環(huán)境中材彪,一個線程在修改array的時候,其他線程是可以進行讀操作的琴儿,只是讀取的array任然是舊數(shù)據(jù)段化。所以對數(shù)據(jù)實時一致性要求高的場景,只能另尋它法造成。
  2. 每次修改都需要進行復制操作穗泵,如果list中存放的大對象,則復制時會產(chǎn)生兩個對象谜疤,會對內(nèi)存產(chǎn)生一定壓力佃延。所以大對象謹慎使用CopyOnWriteArrayList。
CopyOnWriteArraySet

CopyOnWriteArraySet完全基于CopyOnWriteArrayList實現(xiàn)夷磕。部分源碼如下:

public class CopyOnWriteArraySet<E> extends AbstractSet<E>{
//內(nèi)部維護了一個CopyOnWriteArrayList對象
    private final CopyOnWriteArrayList<E> al;

    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }

    public CopyOnWriteArraySet(Collection<? extends E> c) {
        al = new CopyOnWriteArrayList<E>();
        al.addAllAbsent(c);
    }
    //其所有操作都是代理給內(nèi)部的CopyOnWriteArrayList對象執(zhí)行履肃。
    public boolean add(E e) {
        return al.addIfAbsent(e);
    }
    public boolean remove(Object o) {
        return al.remove(o);
    }
    public Iterator<E> iterator() {
        return al.iterator();
    }    
    public Object[] toArray() {
        return al.toArray();
    }
    。坐桩。尺棋。
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绵跷,隨后出現(xiàn)的幾起案子膘螟,更是在濱河造成了極大的恐慌,老刑警劉巖碾局,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荆残,死亡現(xiàn)場離奇詭異,居然都是意外死亡净当,警方通過查閱死者的電腦和手機内斯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門蕴潦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俘闯,你說我怎么就攤上這事潭苞。” “怎么了真朗?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵此疹,是天一觀的道長。 經(jīng)常有香客問我遮婶,道長蝗碎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任蹭睡,我火速辦了婚禮衍菱,結(jié)果婚禮上赶么,老公的妹妹穿的比我還像新娘肩豁。我一直安慰自己,他們只是感情好辫呻,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布清钥。 她就那樣靜靜地躺著,像睡著了一般放闺。 火紅的嫁衣襯著肌膚如雪祟昭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天怖侦,我揣著相機與錄音篡悟,去河邊找鬼。 笑死匾寝,一個胖子當著我的面吹牛搬葬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播艳悔,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼急凰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了猜年?” 一聲冷哼從身側(cè)響起抡锈,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乔外,沒想到半個月后床三,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡杨幼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年勿璃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡补疑,死狀恐怖歧沪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情莲组,我是刑警寧澤诊胞,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站锹杈,受9級特大地震影響撵孤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜竭望,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一邪码、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咬清,春花似錦闭专、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至掘剪,卻和暖如春平委,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背夺谁。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工廉赔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人匾鸥。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓蜡塌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扫腺。 傳聞我的和親對象是個殘疾皇子岗照,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

  • java.util.concurrent.CopyOnWriteArrayList 用于替代同步List,在某些情...
    FX_SKY閱讀 386評論 0 0
  • Java8張圖 11笆环、字符串不變性 12攒至、equals()方法、hashCode()方法的區(qū)別 13躁劣、...
    Miley_MOJIE閱讀 3,709評論 0 11
  • java筆記第一天 == 和 equals ==比較的比較的是兩個變量的值是否相等迫吐,對于引用型變量表示的是兩個變量...
    jmychou閱讀 1,504評論 0 3
  • 對于大學逃課,可能是一種常態(tài)账忘,至少在我們學校是這樣的志膀,而且我也有問過我的一些同學熙宇,似乎大致相同。并且本人也是一...
    流水的中庸閱讀 1,145評論 2 2
  • 接完一個網(wǎng)上的個案咨詢,已是深夜戳稽,卻沒有了睡意馆蠕,我知道是內(nèi)在的愉悅在做崇,因為我發(fā)覺我慢慢的開始佩服自己惊奇,又...
    寧靜牛媽閱讀 639評論 0 0