Java多線程并發(fā)編程中并發(fā)容器第二篇之List的并發(fā)類講解

Java多線程并發(fā)編程中并發(fā)容器第二篇之List的并發(fā)類講解

概述

本文我們將詳細(xì)講解list對(duì)應(yīng)的并發(fā)容器以及用代碼來測(cè)試ArrayList霉晕、vector以及CopyOnWriteArrayList在100個(gè)線程向list中添加1000個(gè)數(shù)據(jù)后的比較

本文是《凱哥分享Java并發(fā)編程之J.U.C包講解》系列教程中的第六篇。如果想系統(tǒng)學(xué)習(xí),凱哥(kaigejava)建議從第一篇開始看风钻。

從本篇開始,我們就來講解講解Java的并發(fā)容器盗尸。大致思路:先介紹什么是并發(fā)容器麸祷。然后講解list相關(guān)的、map相關(guān)的以及隊(duì)列相關(guān)的杭抠。這個(gè)系列會(huì)有好幾篇文章脸甘。大家最好跟著一篇一篇學(xué)。

正文開始

并發(fā)容器分類講解

CopyOneWriteArrayList

Copy-One-Write:即寫入時(shí)候復(fù)制偏灿。

我們知道在原來List子類中vactor是同步容器線程安全的丹诀。這個(gè)CopyOneWriteArrayList可以理解為是他的并發(fā)替代品。

其底層數(shù)據(jù)結(jié)構(gòu)也是數(shù)值翁垂。和ArrayList的不同之處就在于:在list對(duì)象中新增或者是刪除元素的時(shí)候會(huì)把原來的集合copy一份铆遭,增刪操作是在新的對(duì)象中操作的。操作完成之后沿猜,會(huì)將新的數(shù)組替換原來的數(shù)組枚荣。

我們來看看CopyOnWriteArrayList源碼中的add方法:

編輯

?

我們來看看setArray方法:

編輯

?

發(fā)現(xiàn)了嗎?變量使用的是transient和volatile兩個(gè)關(guān)鍵之來修飾的啼肩。

在之前文章中橄妆,我們知道了volatile關(guān)鍵字是內(nèi)存可見性衙伶。那么transient關(guān)鍵字是干嘛的呢?我們來看下百科解釋:

編輯

?

關(guān)鍵的一句話:用transient關(guān)鍵字修飾的成員變量不用參與序列化過程呼畸。

添加注釋后的源碼:

編輯

?

public boolean add(E e) {

final ReentrantLock lock = this.lock;

//獲取到鎖

lock.lock();

try {

//獲取到原集合

Object[] elements = getArray();

int len = elements.length;

//將原集合copy一份到新的集合中痕支。并設(shè)置新的集合的長(zhǎng)度為原集合長(zhǎng)度+1

Object[] newElements = Arrays.copyOf(elements, len + 1);

//將需要新增的元數(shù)添加到新的素組中

newElements[len] = e;

//將新數(shù)組替換原來數(shù)據(jù)。 使用transient和volatitle關(guān)鍵字修飾的

setArray(newElements);

return true;

} finally {

lock.unlock();

}

}

代碼很簡(jiǎn)單蛮原,大致流程如下:

先從ReentrantLock中獲取到鎖(這樣在多線程下可以防止其他線程來修改容器list里面內(nèi)容了)卧须;

通過arrays.copyOf方法copy出一份原有數(shù)組長(zhǎng)度+1;

將要添加的元素賦值給copy出來的數(shù)組儒陨;

使用setArray方法將新得數(shù)組替換原有素組花嘶。

因?yàn)槎际荓ist集合。我們就拿ArrayList蹦漠、vector以及CopyOnWriteArrayList進(jìn)行比較:

ArrayList椭员、vector以及CopyOnWriteArrayList比較

業(yè)務(wù)場(chǎng)景描述:

啟動(dòng)100個(gè)線程,向?qū)ο笾刑砑?000個(gè)數(shù)據(jù)笛园。查看各自運(yùn)行結(jié)果耗時(shí)及插入數(shù)據(jù)總數(shù)隘击。代碼在文章最后凱哥會(huì)貼出來。

先用線程非安全的arrayList執(zhí)行效果:

編輯

?

執(zhí)行arryList時(shí)間為 : 112毫秒研铆!

List.size() : 93266

我們發(fā)現(xiàn)list的長(zhǎng)度不對(duì)埋同。正確的應(yīng)該是100*1000.從結(jié)果來看,arrayList丟數(shù)據(jù)了棵红。

使用Vector執(zhí)行后的效果:

編輯

?

執(zhí)行vector時(shí)間為 : 98毫秒凶赁!

List.size() : 100000

執(zhí)行的總數(shù)對(duì),說下同步鎖沒有丟數(shù)據(jù)逆甜。

在來看看copyOnWriteArrayList執(zhí)行效果:

編輯

?

執(zhí)行copyOnWriteArrayList時(shí)間為 : 5951毫秒虱肄!

List.size() : 100000

運(yùn)行后數(shù)據(jù)比較:

運(yùn)行的類運(yùn)行時(shí)長(zhǎng)運(yùn)行結(jié)果

ArrayList112毫秒93266

Vector98毫秒100000

copyOnWriteArrayList5951毫秒100000

從上面表格中我們可以看出非安全線程的容器會(huì)丟數(shù)據(jù)。使用copyOneWriteArrayList耗時(shí)很長(zhǎng)交煞。那是因?yàn)槊看芜\(yùn)行都要copyof一份咏窿。

總結(jié)

copyArrayList(這里凱哥就簡(jiǎn)寫了):是讀寫分離的。在寫的時(shí)候會(huì)復(fù)制一個(gè)新的數(shù)組來完成插入和修改或者刪除操作之后素征,再將新的數(shù)組給array.讀取的時(shí)候直接讀取最新的數(shù)據(jù)集嵌。

因?yàn)樵趯懙臅r(shí)候需要向主內(nèi)存申請(qǐng)控件,導(dǎo)致寫操作的時(shí)候稚茅,效率非常低的(雖然在操作時(shí)候比較慢得纸淮,但是在刪除或者修改數(shù)組的頭和尾的時(shí)候還是很快的平斩。因?yàn)槠鋽?shù)據(jù)結(jié)構(gòu)決定查找頭和尾快亚享,而且執(zhí)行不需要同步鎖)

從上面表中,可以看出copyArrayList雖然保證了線程的安全性绘面,但是寫操作效率太low了欺税。但是相比Vector來說侈沪,在并發(fā)安全方面的性能要比vector好;

CopyArrayList和Vector相比改進(jìn)的地方:

Vector是在新增晚凿、刪除亭罪、修改以及查詢的時(shí)候都使用了Synchronized關(guān)鍵字來保證同步的。但是每個(gè)方法在執(zhí)行的時(shí)候歼秽,都需要獲取到鎖应役,在獲取鎖等待的過程中性能就會(huì)大大的降低的。

CopyArrayList的改進(jìn):只是在新增和刪除的方法上使用了ReentrantLock鎖進(jìn)行(這里凱哥就不截圖源碼了燥筷,自己可以看看源碼)箩祥。在讀的時(shí)候不加鎖的。所以在讀的方面性能要不vector性能要好肆氓。

所以,CopyArrayList支持讀多寫少的并發(fā)情況

CopyOnWriteArrayList的使用場(chǎng)景:

由于讀操作不加鎖袍祖,增刪改三個(gè)操作加鎖的,因此適用于讀多寫少的場(chǎng)景谢揪,

局限性:因?yàn)樽x的時(shí)候不加鎖的蕉陋,讀的效率和普通的arrayList是一樣的。但是請(qǐng)看讀操作:

編輯

?

編輯

?

在get的時(shí)候array使用的是volatile修飾的拨扶。是內(nèi)存可見的凳鬓。所以可以說copyArrayList在讀的時(shí)候不會(huì)出現(xiàn)arrayList讀取到臟數(shù)據(jù)的問題。

Get(i)方法比較如下:

編輯

?

附件:arrayList屈雄、vector村视、copyOnwriteArrayList比較的代碼:

public static void?main(String[] args) {

//使用線程不安全的arrayList

// List arryList = new ArrayList<>();

//使用vector

// List arryList = new Vector<>();

//使用copyOnWriteArrayList

List arryList =?new?CopyOnWriteArrayList<>();

Random random =?new?Random();

Thread [] threadArr =?new?Thread[100];

CountDownLatch latch =?new?CountDownLatch(threadArr.length);

Long beginTime = System.currentTimeMillis();

for(int?i = 0;ilength;i++){

threadArr[i] =?new?Thread(new?Runnable() {

@Override

public void?run() {

for(int?j = 0; j < 1000; j++){

try?{

arryList.add("value"?+ random.nextInt(100000));

}?catch?(Exception e) {

}

}

latch.countDown();

}

});

}

for(Thread t : threadArr){

t.start();

}

try?{

latch.await();

}?catch?(InterruptedException e) {

e.printStackTrace();

}

long?endTime = System.currentTimeMillis();

System.out.println("執(zhí)行copyOnWriteArrayList時(shí)間為 : "?+ (endTime-beginTime) +?"毫秒!");

System.out.println("List.size() : "?+ arryList.size());

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末酒奶,一起剝皮案震驚了整個(gè)濱河市蚁孔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惋嚎,老刑警劉巖杠氢,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異另伍,居然都是意外死亡鼻百,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門摆尝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來温艇,“玉大人,你說我怎么就攤上這事堕汞∩装” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵讯检,是天一觀的道長(zhǎng)琐鲁。 經(jīng)常有香客問我卫旱,道長(zhǎng),這世上最難降的妖魔是什么围段? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任顾翼,我火速辦了婚禮,結(jié)果婚禮上奈泪,老公的妹妹穿的比我還像新娘适贸。我一直安慰自己,他們只是感情好涝桅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布取逾。 她就那樣靜靜地躺著,像睡著了一般苹支。 火紅的嫁衣襯著肌膚如雪砾隅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天债蜜,我揣著相機(jī)與錄音晴埂,去河邊找鬼。 笑死寻定,一個(gè)胖子當(dāng)著我的面吹牛儒洛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狼速,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼琅锻,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了向胡?” 一聲冷哼從身側(cè)響起恼蓬,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎僵芹,沒想到半個(gè)月后处硬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拇派,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年荷辕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片件豌。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疮方,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出茧彤,到底是詐尸還是另有隱情骡显,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蟆盐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏遭殉。R本人自食惡果不足惜石挂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望险污。 院中可真熱鬧痹愚,春花似錦、人聲如沸蛔糯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蚁飒。三九已至动壤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間淮逻,已是汗流浹背琼懊。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爬早,地道東北人哼丈。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像筛严,于是被迫代替她去往敵國(guó)和親醉旦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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