熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

多線程中使用Java集合類

2022-06-13   來源: Java高級技術 

  Java集合類中某個線程在 Collection 上進行迭代時通常不允許另一個線性修改該 Collection通常在這些情況下迭代的結果是不確定的如果檢測到這種行為一些迭代器實現(包括 JRE 提供的所有通用 collection 實現)可能選擇拋出此異常執行該操作的迭代器稱為快速失敗 迭代器因為迭代器很快就完全失敗而不會冒著在將來某個時間任意發生不確定行為的風險

  因此當一個線程試圖ArrayList的數據的時候另一個線程對ArrayList在進行迭代的會出錯拋出ConcurrentModificationException

  比如下面的代碼

  final List<String> tickets = new ArrayList<String>();

  for (int i = ; i < ; i++) {

     ticketsadd(ticket NO + i);

  }

  Systemoutprintln(start);

  for (int i = ; i < ; i++) {

     Thread salethread = new Thread() {

         public void run() {

             while (ticketssize() > ) {

                 ticketsremove();

                 Systemoutprintln(ThreadcurrentThread()getId()+Remove );

             }

         }

     };

     salethreadstart();

  }

  Systemoutprintln(start);

  new Thread() {

     public void run() {

         for (String s : tickets) {

  Systemoutprintln(s);

         }

     }

  }start();

  上述程序運行後會在某處拋出異常

  javautilConcurrentModificationException

  at javautilArrayList$ItrcheckForComodification(Unknown Source)

  at javautilArrayList$Itrnext(Unknown Source)

  at mytestmytestpkgTj$run(Tjjava:)

  Vector是線程同步的那麼把ArrayList改成Vector是不是就對了呢?

  答案是否定的事實上無論是ArrayList還是Vector只要是實現Collection接口的都要遵循failfast的檢測機制即在迭代是時候不能修改集合的元素一旦發現違法這個規定就會拋出異常

  事實上Vector相對於ArrayList的線程同步體現在對集合元素是否髒讀上即ArrayList允許髒讀而Vector特殊的機制不會出現髒讀但是效率會很差

  舉個例子一個集合個線程從該集合中刪除元素那麼每個元素只可能由一個線程刪除掉不可能會出現一個元素被多個線程刪除的情況

  比如下面的代碼

  final List<String> tickets = new ArrayList<String>();

  for (int i = ; i < ; i++) {

     ticketsadd(ticket NO + i);

  }

  Systemoutprintln(start);

  for (int i = ; i < ; i++) {

     Thread salethread = new Thread() {

         public void run() {

             while (true) {

  if(ticketssize()>)

  Systemoutprintln(ThreadcurrentThread()getId()+ ticketsremove());

  else

  break;

             }

         }

     };

     salethreadstart();

  }

  for循環構造個線程刪除同一個集合中的數據理論上只能刪除但是運行完發現輸出的刪除次數其中很多數據都是被多個線程刪除比如下面的輸出片段

  ticket NO

  ticket NO

  ticket NO

  ticket NO

  ticket NO

  ticket NO

  ticket NO

  ticket NO

  ticket NO

  ticket NO

  ticket NO

  可以看到都被多個線程刪除這事實上就是出現了髒讀解決的辦法就是加鎖使得同一時刻只有個線程對ArrayList做操作

  修改代碼synchronized關鍵字讓得到鎖對象的線程才能運行這樣確保同一時刻只有一個線程操作集合

  final List<String> tickets = new ArrayList<String>();

  for (int i = ; i < ; i++) {

     ticketsadd(ticket NO + i);

  }

  Systemoutprintln(start);

  final Object lock=new Object();

  for (int i = ; i < ; i++) {

     Thread salethread = new Thread() {

         public void run() {

             while (true) {

                 synchronized(lock)

                 {

                   if(ticketssize()>)

                     Systemoutprintln(ThreadcurrentThread()getId()+ ticketsremove());

                   else

                      break;

                 }

             }

         }

     };

     salethreadstart();

  }

  這樣得到的結果就是准確的了

  當然不使用synchronized關鍵字而直接使用vector或者CollectionssynchronizedList 也是同樣效果

  final List<String> tickets =javautilCollectionssynchronizedList(new ArrayList<String>());

  final List<String> tickets =new Vector<String>();

  vector和CollectionssynchronizedList 都是線程同步的避免的髒讀的出現


From:http://tw.wingwit.com/Article/program/Java/gj/201311/27405.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.