Collection-CopyOnWriteArrayList

jdk版本:1.8.0_77
參考文檔:jdk 1.8 docs

CopyOnWriteArrayList類圖

CopyOnWriteArrayList繼承關(guān)系

CopyOnWriteArrayList特點

  1. ArrayList線程安全的擴展版本,所有改變集合的操作(add,set...)需要首先對當(dāng)前數(shù)組制作一份拷貝。
    @Test
    public void testAdd() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        copyOnWriteArrayList.add("1");
        System.out.println(ReflectUtilss.getFieldValue(copyOnWriteArrayList, "array"));
        copyOnWriteArrayList.add("2");
        System.out.println(ReflectUtilss.getFieldValue(copyOnWriteArrayList, "array"));
        copyOnWriteArrayList.add("3");
        System.out.println(ReflectUtilss.getFieldValue(copyOnWriteArrayList, "array"));
    }

運行結(jié)果:

[Ljava.lang.Object;@aec6354
[Ljava.lang.Object;@1c655221
[Ljava.lang.Object;@58d25a40

源代碼:

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);//創(chuàng)建新的數(shù)組對象
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

由此可見,每次添加操作集合中的數(shù)組對象不再是操作之前的數(shù)組對象杠览。

  1. 每次修改拷貝新版本花費巨大动雹,在讀多寫少(相比Vector中方法使用synchronized同步鎖)情況下它是更好的選擇方案靠柑。
    @Test
    public void testCompareCopyOnWriteArrayListandSynchronizedMap() throws InterruptedException {
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            arrayList.add(i);
        }
        List sychronizedList = Collections.synchronizedList(arrayList);
        List copyOnWriteArrayList = new CopyOnWriteArrayList<>(arrayList);
        RunTest[] runTests = new RunTest[64];
        for (int i = 0; i < runTests.length; i++) {
            runTests[i] = new RunTest(sychronizedList);
        }
        for (int i = 0; i < runTests.length; i++) {
            runTests[i].start();
        }
        for (int i = 0; i < runTests.length; i++) {
            runTests[i].join();
        }
        System.out.println(RunTest.TIME);
        RunTest.TIME = 0l;
        for (int i = 0; i < runTests.length; i++) {
            runTests[i] = new RunTest(copyOnWriteArrayList);
        }
        for (int i = 0; i < runTests.length; i++) {
            runTests[i].start();
        }
        for (int i = 0; i < runTests.length; i++) {
            runTests[i].join();
        }
        System.out.println(RunTest.TIME);
    }

運行結(jié)果:

sychronized list run time:12125628
copyonwritearraylist run time:3324206

在設(shè)置64個線程(本機其實是雙核四線程...)執(zhí)行讀取讀取操作躁染,CopyOnWriteArrayList比加鎖的List速度上快了有4倍之多膝捞,在多線程下共享集合(集合不常改變)最好使用CopyOnWriteArrayList瀑凝。

  1. 在創(chuàng)建迭代器時復(fù)制一份數(shù)組拷貝(快照)序芦,在迭代期間數(shù)組不會被改變,調(diào)用remove粤咪、set谚中、add方法拋出UnsupportedOperationException異常。
    @Test
    public void testIterator() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        //COWIterator
        Iterator iterator = copyOnWriteArrayList.iterator();
        //throw UnsupportedOperationException
        iterator.remove();
    }

運行結(jié)果:

java.lang.UnsupportedOperationException
    at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(CopyOnWriteArrayList.java:1176)
        public void remove() {//COWIterator
            throw new UnsupportedOperationException();
        }

之所以這樣設(shè)置是因為CopyOnWrite必須在改變數(shù)組是先制作一份拷貝射窒,所以迭代中不允許移除藏杖、變更元素。

  1. null也可以保存在對象中脉顿。
    @Test
    public void testNull() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        copyOnWriteArrayList.add(null);
    }
  1. 內(nèi)存一致性原則:一個線程向CopyOnWriteArrayList添加對象動作一定happen-before在其之后另一個線程從CopyOnWriteArrayList移除對象蝌麸。
    @Test
    public void testHappenBefore() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        copyOnWriteArrayList.add("test");
        Thread threadB = new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    System.out.println("thread add before:" + ReflectUtilss.getFieldValue(copyOnWriteArrayList, "array"));
                    copyOnWriteArrayList.remove("test");
                    System.out.println("thread add before:" + ReflectUtilss.getFieldValue(copyOnWriteArrayList, "array"));
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                System.out.println("thread B remove elements");
            }
        };
        threadB.start();
        System.out.println("main add before:" + ReflectUtilss.getFieldValue(copyOnWriteArrayList, "array"));
        copyOnWriteArrayList.add("test");//thread A add elements
        System.out.println("main add after:" + ReflectUtilss.getFieldValue(copyOnWriteArrayList, "array"));
        threadB.join();
    }

運行結(jié)果:

main add before:[Ljava.lang.Object;@aec6354
main add after:[Ljava.lang.Object;@1c655221
thread add before:[Ljava.lang.Object;@1c655221
thread add before:[Ljava.lang.Object;@24b8368a
  1. 為了避免每次添加操作都新建一個數(shù)組對象,每次添加時添加一個集合而不是單個對象艾疟。
    @Test
    public void testBetterWay() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        copyOnWriteArrayList.add("test1");//bad
        //good
        ArrayList<Object> list = new ArrayList<>();
        list.add("test2");
        list.add("test3");
        copyOnWriteArrayList.addAll(list);
    }

代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末来吩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蔽莱,更是在濱河造成了極大的恐慌弟疆,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盗冷,死亡現(xiàn)場離奇詭異怠苔,居然都是意外死亡,警方通過查閱死者的電腦和手機仪糖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門柑司,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锅劝,你說我怎么就攤上這事攒驰。” “怎么了故爵?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵玻粪,是天一觀的道長。 經(jīng)常有香客問我,道長劲室,這世上最難降的妖魔是什么伦仍? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮痹籍,結(jié)果婚禮上呢铆,老公的妹妹穿的比我還像新娘。我一直安慰自己蹲缠,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布悠垛。 她就那樣靜靜地躺著线定,像睡著了一般。 火紅的嫁衣襯著肌膚如雪确买。 梳的紋絲不亂的頭發(fā)上斤讥,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音湾趾,去河邊找鬼芭商。 笑死,一個胖子當(dāng)著我的面吹牛搀缠,可吹牛的內(nèi)容都是我干的铛楣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼艺普,長吁一口氣:“原來是場噩夢啊……” “哼簸州!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起歧譬,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤岸浑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瑰步,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矢洲,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年缩焦,在試婚紗的時候發(fā)現(xiàn)自己被綠了读虏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡舌界,死狀恐怖掘譬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呻拌,我是刑警寧澤葱轩,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響靴拱,放射性物質(zhì)發(fā)生泄漏垃喊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一袜炕、第九天 我趴在偏房一處隱蔽的房頂上張望本谜。 院中可真熱鬧,春花似錦偎窘、人聲如沸乌助。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽他托。三九已至,卻和暖如春仆葡,著一層夾襖步出監(jiān)牢的瞬間赏参,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工沿盅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留把篓,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓腰涧,卻偏偏與公主長得像韧掩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子南窗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

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