2010-10-01 44 views
4

我正在閱讀約CopyOnWriteArrayList,並想知道如何在ArrayList類中演示數據競賽。基本上我試圖模擬ArrayList失敗的情況,所以它變得有必要使用CopyOnWriteArrayList。有關如何模擬此的任何建議。Java中的數據競爭ArrayList類

回答

6

種族是當兩個(或多個)線程試圖在共享數據進行操作,並最終輸出取決於所述數據被訪問的順序(和該順序是不確定的)

維基百科:

競賽狀況或種族危害是電子系統或過程中的缺陷,其中過程的輸出和/或結果出乎意料且嚴重依賴於其他事件的順序或時間。該術語源於兩個信號相互競爭以首先影響輸出的想法。

例如:

public class Test { 
    private static List<String> list = new CopyOnWriteArrayList<String>(); 

    public static void main(String[] args) throws Exception { 
     ExecutorService e = Executors.newFixedThreadPool(5); 
     e.execute(new WriterTask()); 
     e.execute(new WriterTask()); 
     e.execute(new WriterTask()); 
     e.execute(new WriterTask()); 
     e.execute(new WriterTask()); 

     e.awaitTermination(20, TimeUnit.SECONDS); 
    } 

    static class WriterTask implements Runnable { 

     @Override 
     public void run() { 
      for (int i = 0; i < 25000; i ++) { 
       list.add("a"); 
      } 
     } 
    } 
} 

然而,這使用ArrayList時,與ArrayIndexOutOfbounds失敗。這是因爲在插入之前應調用ensureCapacity(..)以確保內部陣列可以保存新數據。這裏是發生了什麼:

  • 第一個線程調用add(..),進而調用ensureCapacity(currentSize + 1)
  • 之前的第一個線程實際上已經增加了尺寸,第二個線程還呼籲ensureCapacity(currentSize + 1)
  • 因爲兩者已閱讀的currentSize初始值,內部陣列的新的大小是currentSize + 1
  • 兩個線程使昂貴的操作舊數組複製到擴展之一,新的大小(其不能同時保留兩個添加
  • 然後,他們每個人都試圖將新元素分配到array[size++]。第一個成功,第二個失敗,因爲內部數組由於接收條件而沒有被正確地擴展。

出現這種情況,是因爲兩個線程試圖在同一時間對同一結構中添加項目,並加入其中一個已覆蓋了除其他的(即第一個丟失了)

CopyOnWriteArrayList

  • 多線程的另一個好處寫ArrayList
  • 線程迭代的ArrayList。它一定會得到ConcurrentModificationException

以下是如何證明它:

public class Test { 
    private static List<String> list = new ArrayList<String>(); 

    public static void main(String[] args) throws Exception { 
     ExecutorService e = Executors.newFixedThreadPool(2); 
     e.execute(new WriterTask()); 
     e.execute(new ReaderTask()); 
    } 

    static class ReaderTask implements Runnable { 
     @Override 
     public void run() { 
      while (true) { 
       for (String s : list) { 
        System.out.println(s); 
       } 
      } 
     } 
    } 

    static class WriterTask implements Runnable { 
     @Override 
     public void run() { 
      while(true) { 
       list.add("a"); 
      } 
     } 
    } 
} 

如果你運行該程序多次,你經常會被你得到OutOfMemoryError越來越ConcurrentModificationException之前。

如果用CopyOnWriteArrayList替換它,你沒有得到的異常(但程序很慢)

注意,這僅僅是一個示範 - 的CopyOnWriteArrayList的好處是,當讀取數大大數量超過的寫入次數。

+1

@Mark - 但問題是關於多線程,並且這個例外(如名稱所示)本質上是「多線程的」。 – Bozho 2010-10-01 07:02:25

+0

似乎我並不完全明白。你能否說出數據競賽是如何引起的? – devnull 2010-10-01 07:33:35

+0

@iJeeves看到更新 – Bozho 2010-10-01 08:27:11

-2

兩個線程,一個遞增arraylist和一個遞減。數據競賽可能發生在這裏。

0

實施例:

for (int i = 0; i < array.size(); ++i) { 
    Element elm = array.get(i); 
    doSomethingWith(elm); 
} 

如果另一線程調用array.clear()該線程之前調用array.get(i)中,但它比較了後i相array.size(), - > ArrayIndexOutOfBoundsException異常。