qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          多線程中使用Java集合類

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

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

            比如下面的代碼:

          1. final List<String> tickets = new ArrayList<String>(); 
          2. for (int i = 0; i < 100000; i++) { 
          3.    tickets.add("ticket NO," + i); 
          4. System.out.println("start1..."); 
          5. for (int i = 0; i < 10; i++) { 
          6.    Thread salethread = new Thread() { 
          7.        public void run() { 
          8.            while (tickets.size() > 0) { 
          9.                tickets.remove(0); 
          10.                System.out.println(Thread.currentThread().getId()+"Remove 0"); 
          11.            } 
          12.        } 
          13.    }; 
          14.    salethread.start(); 
          15. System.out.println("start2..."); 
          16. new Thread() { 
          17.    public void run() { 
          18.        for (String s : tickets) { 
          19. System.out.println(s); 
          20.        } 
          21.    } 
          22. }.start();

            上述程序運行后,會在某處拋出異常:

            java.util.ConcurrentModificationException
            at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
            at java.util.ArrayList$Itr.next(Unknown Source)
            at mytest.mytestpkg.Tj$2.run(Tj.java:138)

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

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

            --------------------------------------------------------------------------------

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

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

            比如下面的代碼:

          1. final List<String> tickets = new ArrayList<String>(); 
          2. for (int i = 0; i < 100000; i++) { 
          3.    tickets.add("ticket NO," + i); 
          4. System.out.println("start1..."); 
          5. for (int i = 0; i < 10; i++) { 
          6.    Thread salethread = new Thread() { 
          7.        public void run() { 
          8.            while (true) { 
          9. if(tickets.size()>0
          10. System.out.println(Thread.currentThread().getId()+ tickets.remove(0)); 
          11. else 
          12. break
          13.            } 
          14.        } 
          15.    }; 
          16.    salethread.start(); 
          17. }

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

            17ticket NO,35721
            14ticket NO,35699
            11ticket NO,35721
            18ticket NO,35721
            17ticket NO,35729
            11ticket NO,35729
            14ticket NO,35729
            17ticket NO,35729
            14ticket NO,35734
            17ticket NO,35734
            13ticket NO,35721

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

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

          1. final List<String> tickets = new ArrayList<String>(); 
          2. for (int i = 0; i < 100000; i++) { 
          3.    tickets.add("ticket NO," + i); 
          4. System.out.println("start1..."); 
          5. final Object lock=new Object(); 
          6. for (int i = 0; i < 10; i++) { 
          7.    Thread salethread = new Thread() { 
          8.        public void run() { 
          9.            while (true) { 
          10.                synchronized(lock) 
          11.                { 
          12.                  if(tickets.size()>0
          13.                    System.out.println(Thread.currentThread().getId()+ tickets.remove(0)); 
          14.                  else 
          15.                     break
          16.                } 
          17.            } 
          18.        } 
          19.    }; 
          20.    salethread.start(); 
          21. }

            這樣得到的結果就是準確的了。

            當然,不使用synchronized關鍵字,而直接使用vector或者Collections.synchronizedList 也是同樣效果:

          1. final List<String> tickets =java.util.Collections.synchronizedList(new ArrayList<String>()); 
          2. final List<String> tickets =new Vector<String>();

            vector和Collections.synchronizedList 都是線程同步的,避免的臟讀的出現。




          posted on 2013-02-28 11:33 順其自然EVO 閱讀(312) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2013年2月>
          272829303112
          3456789
          10111213141516
          17181920212223
          242526272812
          3456789

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 馆陶县| 固原市| 二连浩特市| 宁德市| 广汉市| 庆元县| 无锡市| 龙山县| 霍州市| 绍兴县| 广丰县| 陆河县| 防城港市| 灵石县| 无极县| 宁德市| 保亭| 汉源县| 辰溪县| 崇义县| 北川| 西华县| 巴马| 周宁县| 芒康县| 五大连池市| 浦城县| 建水县| 江源县| 黄石市| 加查县| 喀喇| 洞头县| 巫山县| 彩票| 积石山| 葵青区| 江阴市| 陈巴尔虎旗| 固始县| 福州市|