2013-09-24 3 views
5

В нашей заявке мы получили исключение ArrayIndexOutOfBounds в операции ArrayList.add(Object o). Наиболее очевидным объяснением является безопасность потоков, но я не смог воссоздать события. Я попытался создать два потока. В одном я добавляю элементы, а в другой я удаляю их (или очищая массив), но во второй раз я не получал исключение. Я имею в виду, что это может произойти, если посмотреть на источник 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: '' 'add (Object)' '' также может генерировать исключение (я видел это своими глазами), если внутреннее состояние arraylist изменено из другого потока. Проверьте исходный код. – NeplatnyUdaj

+0

yep вы правы: 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

Так что лучше замените 'ArrayList' на параллельную коллекцию, такую ​​как' ConcurrentLinkedQueue' или просто 'Collections.synchronizedList (новый 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 больше не имеет значение, которое было в начале этого метода.

Смежные вопросы