歡迎您光臨本站 註冊首頁

多線程中使用Java集合類

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  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.}

  5.System.out.println("start1...");

  6.for (int i = 0; i < 10; i ) {

  7. Thread salethread = new Thread() {

  8. public void run() {

  9. while (tickets.size() > 0) {

  10. tickets.remove(0);

  11. System.out.println(Thread.currentThread().getId() "Remove 0");

  12. }

  13. }

  14. };

  15. salethread.start();

  16.}

  17.System.out.println("start2...");

  18.new Thread() {

  19. public void run() {

  20. for (String s : tickets) {

  21.System.out.println(s);

  22. }

  23. }

  24.}.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.}

  5.System.out.println("start1...");

  6.for (int i = 0; i < 10; i ) {

  7. Thread salethread = new Thread() {

  8. public void run() {

  9. while (true) {

  10.if(tickets.size()>0)

  11.System.out.println(Thread.currentThread().getId() tickets.remove(0));

  12.else

  13.break;

  14. }

  15. }

  16. };

  17. salethread.start();

  18.}

  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.}

  5.System.out.println("start1...");

  6.final Object lock=new Object();

  7.for (int i = 0; i < 10; i ) {

  8. Thread salethread = new Thread() {

  9. public void run() {

  10. while (true) {

  11. synchronized(lock)

  12. {

  13. if(tickets.size()>0)

  14. System.out.println(Thread.currentThread().getId() tickets.remove(0));

  15. else

  16. break;

  17. }

  18. }

  19. }

  20. };

  21. salethread.start();

  22.}

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

  當然,不使用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 都是線程同步的,避免的臟讀的出現.


[火星人 ] 多線程中使用Java集合類已經有468次圍觀

http://coctec.com/docs/java/show-post-59783.html