2010-04-26 56 views

回答

42

對ArrayList上的兩個線程同時調用add時發生的情況沒有保證行爲。但是,我的經驗是兩個對象都被添加了。與列表相關的大多數線程安全問題在添加/刪除時處理迭代。儘管如此,我強烈建議不要使用具有多線程和併發訪問的vanilla ArrayList。

向量曾經是併發列表的標準,但現在的標準是使用Collections synchronized list

另外,如果您打算隨時用Java中的線程工作,我強烈推薦Goetz等人在實踐中使用Java Concurrency。本書更詳細地介紹了這個問題。

+13

「這是我的經驗,兩個對象已被添加罰款」只是想指出,這只是運氣。 ArrayList數據損壞問題的窗口可能非常小,但它仍然存在 – 2010-04-26 20:32:23

+5

對,這就是我的觀點。絕大多數情況下不會有問題;但我們不會對大多數情況編程。所以我強烈建議不要使用ArrayList。 – derivation 2010-04-26 21:03:02

2

如果你想要線程安全的arrayList,你可以使用List l = Collections.synchronizedList(new ArrayList());

0

http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html

注意,此實現不是同步的。如果多個線程同時訪問ArrayList實例,並且至少有一個線程在結構上修改了列表,則它必須在外部同步。

由於內部沒有同步,所以你推理的東西並不合理。

因此,事情變得不同步,帶來不愉快和不可預知的結果。

+0

對,看到了。我曾經想過會有一個'ConcurrentModificationException'會被拋出,這對我們來說並沒有發生..也許我們遇到了另一個不使用線程安全集合的影響.. – 2010-04-26 18:52:17

+0

@Marcus似乎只是如果你在迭代它的同時修改列表。此外,不保證此異常將被拋出。 – WhirlWind 2010-04-26 18:53:51

1

java.util.concurrent有一個線程安全的數組列表。標準ArrayList不是線程安全的,並且多線程同時更新時的行爲未定義。當一個或多個線程同時寫入時,多讀者也可能會出現奇怪的行爲。

3

你也可以得到一個null,一個ArrayOutOfBoundsException,或一些遺留下來的實施。已經觀察到在生產系統中進入無限循環。你不需要知道什麼可能會出錯,只是不要這樣做。

您可以使用Vector,但它傾向於解決接口不夠豐富。在大多數情況下,你可能會發現你想要一個不同的數據結構。

3

該行爲可能是未定義的,因爲ArrayList不是線程安全的。如果在Iterator進行交互時修改列表,則會得到ConcurrentModificationException。您可以使用Collection.synchronizedList封裝ArrayList,或使用線程安全的集合(有很多),或者將add調用放入同步塊中。

14

任何數量的事情都可能發生。您可以正確添加這兩個對象。您只能添加其中一個對象。你可能會得到一個ArrayIndexOutOfBounds異常,因爲底層數組的大小沒有適當調整。或者可能發生其他事情。只要說你不能依賴任何發生的行爲就夠了。

作爲替代方案,您可以使用Vector,您可以使用Collections.synchronizedList,您可以使用CopyOnWriteArrayList,或者您可以使用單獨的鎖。這一切都取決於你在做什麼以及對訪問該集合有何種控制。

3

我想出了下面的代碼來模仿一些真實的世界場景。

100個任務並行運行,他們將完成狀態更新到主程序。我使用CountDownLatch來等待任務完成。

import java.util.concurrent.*; 
import java.util.*; 

public class Runner { 

    // Should be replaced with Collections.synchronizedList(new ArrayList<Integer>()) 
    public List<Integer> completed = new ArrayList<Integer>(); 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     Runner r = new Runner(); 
     ExecutorService exe = Executors.newFixedThreadPool(30); 
     int tasks = 100; 
     CountDownLatch latch = new CountDownLatch(tasks); 
     for (int i = 0; i < tasks; i++) { 
      exe.submit(r.new Task(i, latch)); 
     } 
     try { 
      latch.await(); 
      System.out.println("Summary:"); 
      System.out.println("Number of tasks completed: " 
        + r.completed.size()); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     exe.shutdown(); 
    } 

    class Task implements Runnable { 

     private int id; 
     private CountDownLatch latch; 

     public Task(int id, CountDownLatch latch) { 
      this.id = id; 
      this.latch = latch; 
     } 

     public void run() { 
      Random r = new Random(); 
      try { 
       Thread.sleep(r.nextInt(5000)); //Actual work of the task 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      completed.add(id); 
      latch.countDown(); 
     } 
    } 
} 

,當我跑的應用10倍,至少3〜4次的方案並沒有打印的已完成的任務數正確。理想情況下,它應該打印100(如果沒有例外發生)。但在某些情況下,它正在打印98,99等。

因此,它證明ArrayList的併發更新不會給出正確的結果。

如果我用同步版本替換ArrayList,程序輸出正確的結果。

0

你也可以使用的ArrayList();

Collections.synchronizedList(new ArrayList()); 

new Vector(); 

synchronizedList因爲我最好的,因爲它是:

  • 快上50-100%
  • 能與已有的Arra合作yList的
相關問題