Java多線程理解:線程安全的集合對(duì)象

1、概念介紹

成都創(chuàng)新互聯(lián)成立與2013年,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目做網(wǎng)站、成都網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元中寧做網(wǎng)站,已為上家服務(wù),為中寧各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220

  • 線程安全就是多線程訪問時(shí),采用了加鎖機(jī)制,當(dāng)一個(gè)線程訪問該類的某個(gè)數(shù)據(jù)時(shí),進(jìn)行保護(hù),其他線程不能進(jìn)行訪問直到該線程讀取完,其他線程才可使用。不會(huì)出現(xiàn)數(shù)據(jù)不一致或者數(shù)據(jù)污染。

  • 線程不安全就是不提供數(shù)據(jù)訪問保護(hù),多線程先后更改數(shù)據(jù)會(huì)產(chǎn)生數(shù)據(jù)不一致或者數(shù)據(jù)污染的情況。

  • 一般使用synchronized關(guān)鍵字加鎖同步控制,來解決線程不安全問題。

2、線程安全的集合對(duì)象

  • ArrayList線程不安全,Vector線程安全;

  • HashMap線程不安全,HashTable線程安全;

  • StringBuilder線程不安全,StringBuffer線程安全。

3、代碼測(cè)試

  • ArrayList線程不安全:
    在主線程中新建100個(gè)子線程,分別向ArrayList中添加100個(gè)元素,最后打印ArrayList的size。

public?class?Test?{??public?static?void?main(String?[]?args){??????//?用來測(cè)試的List??
??????List<String>?data?=?new?ArrayList<>();??????//?用來讓主線程等待100個(gè)子線程執(zhí)行完畢??
??????CountDownLatch?countDownLatch?=?new?CountDownLatch(100);??????//?啟動(dòng)100個(gè)子線程??
??????for(int?i=0;i<100;i++){
??????????SampleTask?task?=?new?SampleTask(data,countDownLatch);
??????????Thread?thread?=?new?Thread(task);
??????????thread.start();
??????}??????try{??????????//?主線程等待所有子線程執(zhí)行完成,再向下執(zhí)行??
??????????countDownLatch.await();
??????}catch?(InterruptedException?e){??
??????????e.printStackTrace();??
??????}?
??????//?List的size??
??????System.out.println(data.size());
??}
}class?SampleTask?implements?Runnable?{
????CountDownLatch?countDownLatch;
????List<String>?data;????public?SampleTask(List<String>?data,CountDownLatch?countDownLatch){????????this.data?=?data;????????this.countDownLatch?=?countDownLatch;
????}????@Override
????public?void?run()?{????????//?每個(gè)線程向List中添加100個(gè)元素??
????????for(int?i?=?0;?i?<?100;?i++)??
????????{??
????????????data.add("1");
????????}??
????????//?完成一個(gè)子線程??
????????countDownLatch.countDown();
????}
}

7次測(cè)試輸出():

99981000010000ArrayIndexOutOfBoundsException1000099679936
  • Vector線程安全:
    在主線程中新建100個(gè)子線程,分別向Vector中添加100個(gè)元素,最后打印Vector的size。

public?class?Test?{??public?static?void?main(String?[]?args){??????//?用來測(cè)試的List??
??????List<String>?data?=?new?Vector<>();??????//?用來讓主線程等待100個(gè)子線程執(zhí)行完畢??
??????CountDownLatch?countDownLatch?=?new?CountDownLatch(100);??????//?啟動(dòng)100個(gè)子線程??
??????for(int?i=0;i<100;i++){
??????????SampleTask?task?=?new?SampleTask(data,countDownLatch);
??????????Thread?thread?=?new?Thread(task);
??????????thread.start();
??????}??????try{??????????//?主線程等待所有子線程執(zhí)行完成,再向下執(zhí)行??
??????????countDownLatch.await();
??????}catch?(InterruptedException?e){??
??????????e.printStackTrace();??
??????}?
??????//?List的size??
??????System.out.println(data.size());
??}
}class?SampleTask?implements?Runnable?{
????CountDownLatch?countDownLatch;
????List<String>?data;????public?SampleTask(List<String>?data,CountDownLatch?countDownLatch){????????this.data?=?data;????????this.countDownLatch?=?countDownLatch;
????}????@Override
????public?void?run()?{????????//?每個(gè)線程向List中添加100個(gè)元素??
????????for(int?i?=?0;?i?<?100;?i++)??
????????{??
????????????data.add("1");
????????}??
????????//?完成一個(gè)子線程??
????????countDownLatch.countDown();
????}
}

7次測(cè)試輸出():

10000100001000010000100001000010000
  • 使用synchronized關(guān)鍵字來同步ArrayList:

public?class?Test?{??public?static?void?main(String?[]?args){??????//?用來測(cè)試的List??
??????List<String>?data?=?new?ArrayList<>();??????//?用來讓主線程等待100個(gè)子線程執(zhí)行完畢??
??????CountDownLatch?countDownLatch?=?new?CountDownLatch(100);??????//?啟動(dòng)100個(gè)子線程??
??????for(int?i=0;i<100;i++){
??????????SampleTask?task?=?new?SampleTask(data,countDownLatch);
??????????Thread?thread?=?new?Thread(task);
??????????thread.start();
??????}??????try{??????????//?主線程等待所有子線程執(zhí)行完成,再向下執(zhí)行??
??????????countDownLatch.await();
??????}catch?(InterruptedException?e){??
??????????e.printStackTrace();??
??????}?
??????//?List的size??
??????System.out.println(data.size());
??}
}class?SampleTask?implements?Runnable?{
????CountDownLatch?countDownLatch;
????List<String>?data;????public?SampleTask(List<String>?data,CountDownLatch?countDownLatch){????????this.data?=?data;????????this.countDownLatch?=?countDownLatch;
????}????@Override
????public?void?run()?{????????//?每個(gè)線程向List中添加100個(gè)元素??
????????for(int?i?=?0;?i?<?100;?i++)??
????????{??
????????????synchronized(data){
????????????????data.add("1");
????????????}
????????}??
????????//?完成一個(gè)子線程??
????????countDownLatch.countDown();
????}
}

7次測(cè)試輸出():

10000100001000010000100001000010000

3、原因分析

  • ArrayList在添加一個(gè)元素的時(shí)候,它會(huì)有兩步來完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
    在單線程運(yùn)行的情況下,如果 Size = 0,添加一個(gè)元素后,此元素在位置 0,而且 Size=1;
    而如果是在多線程情況下,比如有兩個(gè)線程,線程 A 先將元素1存放在位置 0。但是此時(shí) CPU 調(diào)度線程A暫停,線程 B 得到運(yùn)行的機(jī)會(huì)。線程B向此 ArrayList 添加元素2,因?yàn)榇藭r(shí) Size 仍然等于 0 (注意,我們假設(shè)的是添加一個(gè)元素是要兩個(gè)步驟,而線程A僅僅完成了步驟1),所以線程B也將元素存放在位置0。然后線程A和線程B都繼續(xù)運(yùn)行,都增加 Size 的值,結(jié)果Size都等于1。
    最后,ArrayList中期望的元素應(yīng)該有2個(gè),而實(shí)際元素是在0位置,造成丟失元素,故Size 等于1。導(dǎo)致“線程不安全”。
    ArrayList源碼:

@Override?public?boolean?add(E?object)?{
????????Object[]?a?=?array;????????int?s?=?size;????????if?(s?==?a.length)?{
????????????Object[]?newArray?=?new?Object[s?+
????????????????????(s?<?(MIN_CAPACITY_INCREMENT?/?2)??
?????????????????????MIN_CAPACITY_INCREMENT?:?s?>>?1)];
????????????System.arraycopy(a,?0,?newArray,?0,?s);
????????????array?=?a?=?newArray;
????????}
????????a[s]?=?object;
????????size?=?s?+?1;
????????modCount++;????????return?true;
????}
  • Vector的所有操作方法都被同步了,既然被同步了,多個(gè)線程就不可能同時(shí)訪問vector中的數(shù)據(jù),只能一個(gè)一個(gè)地訪問,所以不會(huì)出現(xiàn)數(shù)據(jù)混亂的情況,所以是線程安全的。
    Vector源碼:

@Override
????public?synchronized?boolean?add(E?object)?{????????if?(elementCount?==?elementData.length)?{
????????????growByOne();
????????}
????????elementData[elementCount++]?=?object;
????????modCount++;????????return?true;
????}

4、線程安全的集合并不安全

分析以下場(chǎng)景:

synchronized(map){
Object?value?=?map.get(key);if(value?==?null)
{
????value?=?new?Object();
????map.put(key,value);
}return?value;}

由于線程安全的集合對(duì)象是基于單個(gè)方法的同步,所以即使map是線程安全的,也會(huì)產(chǎn)生不同步現(xiàn)象。
在非單個(gè)方法的場(chǎng)景下,我們?nèi)匀恍枰褂胹ynchronized加鎖才能保證對(duì)象的同步。

代碼測(cè)試:

public?class?Test?{??public?static?void?main(String?[]?args){??????//?用來測(cè)試的List??
??????List<String>?data?=?new?Vector<>();??????//?用來讓主線程等待100個(gè)子線程執(zhí)行完畢??
??????CountDownLatch?countDownLatch?=?new?CountDownLatch(100);??????//?啟動(dòng)100個(gè)子線程??
??????for(int?i=0;i<1000;i++){
??????????SampleTask?task?=?new?SampleTask(data,countDownLatch);
??????????Thread?thread?=?new?Thread(task);
??????????thread.start();
??????}??????try{??????????//?主線程等待所有子線程執(zhí)行完成,再向下執(zhí)行??
??????????countDownLatch.await();
??????}catch?(InterruptedException?e){??
??????????e.printStackTrace();??
??????}?
??????//?List的size??
??????System.out.println(data.size());
??}
}class?SampleTask?implements?Runnable?{
????CountDownLatch?countDownLatch;
????List<String>?data;????public?SampleTask(List<String>?data,CountDownLatch?countDownLatch){????????this.data?=?data;????????this.countDownLatch?=?countDownLatch;
????}????@Override
????public?void?run()?{????????//?每個(gè)線程向List中添加100個(gè)元素??
????????int?size?=?data.size();
????????data.add(size,"1");?
????????//?完成一個(gè)子線程??
????????countDownLatch.countDown();
????}
}
997
993
995
996
997
998
997

5、總結(jié)

  • 如何取舍
    線程安全必須要使用synchronized關(guān)鍵字來同步控制,所以會(huì)導(dǎo)致性能的降低。
    當(dāng)不需要線程安全時(shí),可以選擇ArrayList,避免方法同步產(chǎn)生的開銷;
    當(dāng)多個(gè)線程操作同一個(gè)對(duì)象時(shí),可以選擇線程安全的Vector;

  • 線程不安全!=不安全
    有人在使用過程中有一個(gè)不正確的觀點(diǎn):我的程序是多線程的,不能使用ArrayList要使用Vector,這樣才安全。
    線程不安全并不是多線程環(huán)境下就不能使用。
    注意線程不安全條件:多線程操作同一個(gè)對(duì)象。比如上述代碼就是在多個(gè)線程操作同一個(gè)ArrayList對(duì)象。
    如果每個(gè)線程中new一個(gè)ArrayList,而這個(gè)ArrayList只在這一個(gè)線程中使用,那么是沒問題的。

  • 線程‘安全’的集合對(duì)象
    較復(fù)雜的操作下,線程安全的集合對(duì)象也無法保證數(shù)據(jù)的同步,仍然需要我們來處理。

分享名稱:Java多線程理解:線程安全的集合對(duì)象
標(biāo)題來源:http://bm7419.com/article18/igohgp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)、手機(jī)網(wǎng)站建設(shè)App設(shè)計(jì)、網(wǎng)站改版外貿(mào)建站、標(biāo)簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司