2013-09-24 66 views
5

在我們的應用程序中,我們在ArrayList.add(Object o)操作中得到了一個ArrayIndexOutOfBounds異常。最明顯的解釋是線程安全,但我無法重新創建事件。我試過創建兩個線程。在一個我添加元素,在另一個我刪除它們(或清除數組),但我沒有得到第二次例外。 我的意思是它很明顯可以通過查看ArrayList的來源發生,但能夠證明它是很好的。如何證明arraylist是不是線程安全的測試?

我一直在運行這個測試相當長一段時間沒有任何異常:

public class Test { 
static ArrayList a = new ArrayList(); 

public static void main(String[] args) throws Exception { 
    Thread t1 = new Thread() { 
     public void run() { 
      while (true) { 
       if (a.size() > 0) 
        a.remove(0); 
      } 
     } 
    }; 

    Thread t2 = new Thread() { 
     public void run() { 
      while (true) { 
       a.add(new Object()); 
      } 
     } 
    }; 

    t2.start(); 
    Thread.sleep(100); 
    t1.start(); 
} 
} 
+0

'ArrayList.add(Object)'不會拋出'ArrayIndexOutOfBoundsException'; 'ArrayList.add(index,Object)'會。 –

+0

guido:如果arraylist的內部狀態從其他線程改變,那麼add(Object)'''也可以拋出異常(我親眼看到它)。檢查源代碼。 – NeplatnyUdaj

+0

是的,你是對的:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.add%28java.lang .Object%29;它必須在外部同步 –

回答

5

由於從isnot2bad發表評論我發現了一個問題,我的假設。 問題在於併發添加,而不是添加/刪除。 我能創造一個失敗的測試:

static ArrayList a = new ArrayList(1); 

public static void main(String[] args) throws Exception { 
    Thread t1 = new Thread() { 
     public void run() { 
      while (true) { 
       a.add(new Object()); 
      } 
     } 
    }; 

    Thread t2 = new Thread() { 
     public void run() { 
      while (true) { 
       a = new ArrayList(1); 
       a.add(new Object()); 
       a.add(new Object()); 
      } 
     } 
    }; 

    t2.start(); 
    Thread.sleep(100); 
    t1.start(); 
} 

在有在第一線的添加行,我得到這樣的:

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 2 

:)

+1

所以最好在你的項目中用像'ConcurrentLinkedQueue'這樣的併發集合或簡單的'Collections.synchronizedList(new ArrayList())'代替'ArrayList'! ;) – isnot2bad

+0

在這個特定的情況下是的。我知道問題在哪裏。但之前從未見過。 – NeplatnyUdaj

1

我能通過添加更多加法器線程來重現您的問題。

0

使您的消費者線程睡眠遠少於生產者睡眠,例如, 20毫秒而不是100毫秒。這樣,拋出異常的機會就大得多。

+0

線程未處於睡眠狀態。這只是在開始第二個線程之前的延遲。無論如何,代碼不會產生所需的行爲 – NeplatnyUdaj

2

可能很難觀察到給定代碼的任何錯誤,因爲您實際上並未檢查列表中存儲了什麼。我不能說不可能得到一個ArrayIndexOutOfBoundsException,但它將是非常罕見的,因爲只有當數組被調整大小時才能得到一個,並且它很少調整大小。

如果您檢查您刪除沒有對象複製它更可能看到意外的行爲:只添加新的對象,所以,消除線程永遠不會看到同樣的對象兩次,對不對?事實並非如此:

import java.util.*; 
public class Test { 
    static ArrayList a = new ArrayList(); 

    public static void main(String[] args) throws Exception { 
     Thread t1 = new Thread() { 
      public void run() { 
       Object x = null; 
       while (true) { 
        if (a.size() > 0) { 
         Object y = a.remove(0); 
         if (x == y) System.out.println("Duplicate!"); 
         x = y; 
        } 
       } 
      } 
     }; 

     Thread t2 = new Thread() { 
      public void run() { 
       while (true) { 
        a.add(new Object()); 
       } 
      } 
     }; 

     t2.start(); 
     Thread.sleep(100); 
     t1.start(); 
    } 
} 

這種情況發生在System.arrayCopy通話過程中添加的對象:elementData[--size] = null設置錯誤的數組索引null因爲size不再有它在該方法的開始值。

相關問題