2013-09-24 10 views
5

W naszej aplikacji otrzymaliśmy wyjątek ArrayIndexOutOfBounds w operacji ArrayList.add(Object o). Najbardziej oczywistym wytłumaczeniem jest bezpieczeństwo wątków, ale nie byłem w stanie odtworzyć zdarzeń. Próbowałem utworzyć dwa wątki. W jednym dodaję elementy, w drugim usuwam je (lub czyszczę tablicę), ale nie dostałem wyjątku po raz drugi. Mam na myśli to, że może się zdarzyć, patrząc na źródło ArrayList, ale byłoby miło móc to zademonstrować.Jak udowodnić, że lista kontrolna nie jest bezpieczna dla wątków za pomocą testu?

ja już działa ten test od dłuższego czasu bez wyjątku:

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)' nie rzuci 'ArrayIndexOutOfBoundsException'; "ArrayList.add (index, Object)' będzie. –

+0

guido: '' 'add (Object)' '' może również wyrzucić wyjątek (widziałem to na własne oczy), jeśli stan wewnętrzny listy jest zmieniony z innego wątku. Sprawdź kod źródłowy. – NeplatnyUdaj

+0

Tak, masz prawo: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.add%28java.lang .Object% 29; musi być zsynchronizowany externally –

Odpowiedz

5

Dzięki Komentarz isnot2bad znalazłem problem w moich założeń. Problem dotyczy dodawania współbieżnego, a nie dodawania/usuwania. udało mi się stworzyć uszkodzoną Test:

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(); 
} 

Na linii z dodatku na pierwszym wątku, jestem coraz to:

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

:)

+1

Lepiej zamień "ArrayList" na kolekcję współbieżną, jak "ConcurrentLinkedQueue" lub po prostu 'Collections.synchronizedList (new ArrayList())' w twoim projekcie! ;) – isnot2bad

+0

W tym konkretnym przypadku tak. Wiem, gdzie jest problem.Ale nigdy tego nie widziałem. – NeplatnyUdaj

1

jestem w stanie aby odtworzyć swój problem po prostu dodając więcej wątków dodawania.

0

Spraw, by wątek konsumencki spał znacznie krócej niż sen producenta, np. 20 ms zamiast 100 ms. W ten sposób szanse na wyrzucenie wyjątku są znacznie większe.

+0

Nici nie śpią. To tylko opóźnienie przed uruchomieniem drugiego wątku. W każdym razie ten kod nie wywołał pożądanego zachowania – NeplatnyUdaj

2

Może być trudno zaobserwować jakikolwiek błąd z podanym kodem, ponieważ tak naprawdę nie sprawdzasz, co jest zapisane na liście. Nie mogę powiedzieć, że niemożliwe jest uzyskanie ArrayIndexOutOfBoundsException, ale będzie to bardzo rzadkie, ponieważ można je zdobyć tylko wtedy, gdy zmieniana jest wielkość tablicy i jest ona bardzo rzadko zmieniana.

Jeśli sprawdzisz, że usuwane obiekty nie są duplikatami, znacznie bardziej prawdopodobne jest zobaczenie nieoczekiwanego zachowania: dodajesz tylko nowe obiekty, więc wątek, który usuwa, nigdy nie powinien widzieć tego samego obiektu dwa razy, prawda? Nie tak:

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(); 
    } 
} 

Dzieje się tak, gdy jest dodawany obiekt podczas rozmowy System.arrayCopy: elementData[--size] = null ustawia niewłaściwy indeks tablicy do null ponieważ size nie ma już wartości, jaką miał na początku metody.

Powiązane problemy