摘要:前言今天我們一起學(xué)習(xí)下并發(fā)包里的工具類。的定義如下它也屬于集合框架的一部分,是的線程安全的變體,跟的不同在于針對(duì)數(shù)組的修改操作等是基于內(nèi)部拷貝的一份數(shù)據(jù)而進(jìn)行的。發(fā)生修改時(shí)候做,新老版本分離,保證讀的高性能,適用于以讀為主的情況。
前言
今天我們一起學(xué)習(xí)下java.util.concurrent并發(fā)包里的CopyOnWriteArrayList工具類。當(dāng)有多個(gè)線程可能同時(shí)遍歷、修改某個(gè)公共數(shù)組時(shí)候,如果不希望因使用synchronize關(guān)鍵字鎖住整個(gè)數(shù)組而影響性能,可以考慮使用CopyOnWriteArrayList。
CopyOnWriteArrayList APICopyOnWriteArrayList的定義如下:
public class CopyOnWriteArrayListextends Object implements List , RandomAccess, Cloneable, Serializable
它也屬于Java集合框架的一部分,是[ArrayList]()的線程安全的變體,跟ArrayList的不同在于:CopyOnWriteArrayList針對(duì)數(shù)組的修改操作(add、set等)是基于內(nèi)部拷貝的一份數(shù)據(jù)而進(jìn)行的。換句話說,即使在一個(gè)線程進(jìn)行遍歷操作時(shí)有其他線程可能進(jìn)行插入或刪除操作,我們也可以“線程安全”得遍歷CopyOnWriteArrayList。
例子1:插入(刪除)數(shù)據(jù)的同時(shí)進(jìn)行遍歷CopyOnWriteArrayList的實(shí)現(xiàn)原理是,在一個(gè)線程開始遍歷(創(chuàng)建Iterator對(duì)象)時(shí),內(nèi)部會(huì)創(chuàng)建一個(gè)“快照”數(shù)組,遍歷基于這個(gè)快照Iterator進(jìn)行,在遍歷過程中這個(gè)快照數(shù)組不會(huì)改變,也就不會(huì)拋出ConcurrentModificationException。如果在遍歷的過程中有其他線程嘗試改變數(shù)組的內(nèi)容,就會(huì)拷貝一份新的數(shù)據(jù)進(jìn)行變更,而后面再來訪問這個(gè)數(shù)組的線程,看到的就是變更過的數(shù)組。
創(chuàng)建一個(gè)CopyOnWriteArrayList數(shù)組numbers;
CopyOnWriteArrayListnumbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});
?
創(chuàng)建一個(gè)遍歷器iterator;
Iteratoriterator = numbers.iterator();
?
給numbers中增加(或刪除、修改)一個(gè)元素;
numbers.add(100);
?
利用iterator遍歷數(shù)組的元素,發(fā)現(xiàn)遍歷的結(jié)果是Iterator對(duì)象創(chuàng)建之前的;
Listresult = new LinkedList<>(); iterator.forEachRemaining(result::add); assertThat(result).containsOnly(1, 3, 5, 78);
完整的例子如下:
package org.java.learn.concurrent.copyonwritearraylist; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import static org.assertj.core.api.Assertions.*; /** * 作用: * User: duqi * Date: 2017/11/9 * Time: 11:20 */ public class CopyOnWriteArrayListExample { public static void main(String[] args) { CopyOnWriteArrayList例子2:不支持一邊遍歷一邊刪除numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78}); Iterator iterator = numbers.iterator(); numbers.add(100); List result = new LinkedList<>(); iterator.forEachRemaining(result::add); assertThat(result).containsOnly(1, 3, 5, 78); Iterator iterator2 = numbers.iterator(); numbers.remove(3); List result2 = new LinkedList<>(); iterator2.forEachRemaining(result2::add); assertThat(result2).containsOnly(1, 3, 5, 78, 100); } }
由于CopyOnWriteArrayList的實(shí)現(xiàn)機(jī)制——>修改操作和讀操作拿到的Iterator對(duì)象指向的不是一個(gè)數(shù)組,因此不支持基于Iterator對(duì)象的方法結(jié)果的刪除:public void remove();,例子代碼如下:
package org.java.learn.concurrent.copyonwritearraylist; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; /** * 作用: User: duqi Date: 2017/11/9 Time: 13:40 */ public class CopyOnWriteArrayListExample2 { public static void main(String[] args) { try { testExceptionThrow(); } catch (Exception e) { e.printStackTrace(); } } private static void testExceptionThrow() { CopyOnWriteArrayList結(jié)論numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78}); Iterator integerIterator = numbers.iterator(); while (integerIterator.hasNext()) { integerIterator.remove(); } } }
CopyOnWriteArrayList適合使用在讀操作遠(yuǎn)遠(yuǎn)大于寫操作的場(chǎng)景里,比如緩存。發(fā)生修改時(shí)候做copy,新老版本分離,保證讀的高性能,適用于以讀為主的情況。
參考資料Guide to CopyOnWriteArrayList
CopyOnWriteArrayList詳解
官方文檔:CopyOnWriteArrayList
本號(hào)專注于后端技術(shù)、JVM問題排查和優(yōu)化、Java面試題、個(gè)人成長和自我管理等主題,為讀者提供一線開發(fā)者的工作和成長經(jīng)驗(yàn),期待你能在這里有所收獲。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75532.html
摘要:提供了線程安全的共享對(duì)象,在編寫多線程代碼時(shí),可把不安全的整個(gè)變量封裝進(jìn),或者把該對(duì)象與線程相關(guān)的狀態(tài)使用保存并不能替代同步機(jī)制,兩者面向的問題領(lǐng)域不同。 ThreadLocal類 使用ThreadLocal類可以簡化多線程編程時(shí)的并發(fā)訪問,使用這個(gè)工具類可以很簡捷地隔離多線程程序的競(jìng)爭資源。Java5之后,為ThreadLocal類增加了泛型支持,即ThreadLocal Threa...
摘要:同步容器及其注意事項(xiàng)中的容器主要可以分為四個(gè)大類,分別是和,但并不是所有的容器都是線程安全的。并發(fā)容器及其注意事項(xiàng)在版本之前所謂的線程安全的容器,主要指的就是同步容器,當(dāng)然因?yàn)樗蟹椒ǘ加脕肀WC互斥,串行度太高了,性能太差了。 Java 并發(fā)包有很大一部分內(nèi)容都是關(guān)于并發(fā)容器的,因此學(xué)習(xí)和搞懂這部分的內(nèi)容很有必要。 Java 1.5 之前提供的同步容器雖然也能保證線程安全,但是性能很差...
摘要:今天主要講解的是本文力求簡單講清每個(gè)知識(shí)點(diǎn),希望大家看完能有所收獲一和回顧線程安全的和我們知道是用于替代的,是線程安全的容器。使用迭代器遍歷時(shí)不需要顯示加鎖,看看與方法的實(shí)現(xiàn)可能就有點(diǎn)眉目了。 前言 只有光頭才能變強(qiáng) showImg(https://segmentfault.com/img/remote/1460000016931828?w=1120&h=640); 前一陣子寫過一篇C...
摘要:體現(xiàn)的就是適配器模式。數(shù)組對(duì)象集合世界中的機(jī)制機(jī)制集合世界中比較常見的錯(cuò)誤檢測(cè)機(jī)制,防止在對(duì)集合進(jìn)行遍歷過程當(dāng)中,出現(xiàn)意料之外的修改,會(huì)通過異常暴力的反應(yīng)出來。而在增強(qiáng)循環(huán)中,集合遍歷是通過進(jìn)行的。 前言 學(xué)習(xí)情況記錄 時(shí)間:week 2 SMART子目標(biāo) :Java 容器 記錄在學(xué)習(xí)Java容器 知識(shí)點(diǎn)中,關(guān)于List的重點(diǎn)知識(shí)點(diǎn)。 知識(shí)點(diǎn)概覽: 容器中的設(shè)計(jì)模式 從Array...
摘要:并發(fā)數(shù)據(jù)結(jié)構(gòu)存在的理由串行數(shù)據(jù)結(jié)構(gòu)在并發(fā)環(huán)境下是不安全的,而直接使用鎖又會(huì)帶來性能的影響,所以專門設(shè)計(jì)了針對(duì)并發(fā)環(huán)境下的數(shù)據(jù)結(jié)構(gòu),其中使用了無鎖運(yùn)算來保證性能。在高并發(fā)的情況下過多的鎖操作會(huì)拖累系統(tǒng)的性能。是由數(shù)組結(jié)構(gòu)和數(shù)組結(jié)構(gòu)組成。 【并發(fā)數(shù)據(jù)結(jié)構(gòu)存在的理由 串行數(shù)據(jù)結(jié)構(gòu)在并發(fā)環(huán)境下是不安全的,而直接使用鎖又會(huì)帶來性能的影響,所以jdk專門設(shè)計(jì)了針對(duì)并發(fā)環(huán)境下的數(shù)據(jù)結(jié)構(gòu),其中使用了無...
閱讀 3892·2021-10-08 10:05
閱讀 2976·2021-09-27 13:57
閱讀 2700·2019-08-29 11:32
閱讀 1023·2019-08-28 18:18
閱讀 1317·2019-08-28 18:05
閱讀 2003·2019-08-26 13:39
閱讀 881·2019-08-26 11:37
閱讀 2064·2019-08-26 10:37