HashMap и видимость
HashMap»с Javadoc состояния:
если карта структурно изменена в любое время после создания итератора, любым способом, кроме как через итераторСобственный метод удаления, итератор сгенерирует исключение ConcurrentModificationException.
Я создал пример кода, который, основываясь на спецификации, должен почти сразу завершиться сбоем, и выдал исключение ConcurrentModificationException;
Сбой сразу же, как и ожидалось с Java 7но он (кажется) всегда работает с Java 6 (то есть он не выдает обещанное исключение).Примечание: иногда это не дает сбоя в Java 7 (скажем, 1 раз из 20) - я полагаю, это связано с планированием потоков (то есть, 2 запускаемых объекта не чередуются).
Я что-то пропустил? Почему версия, запускаемая с Java 6, не генерирует исключение ConcurrentModificationException?
По сути, есть 2 запускаемые задачи, выполняющиеся параллельно (обратный отсчет используется, чтобы запустить их примерно одновременно):
один добавляет элементы на картудругая перебирает карту, читает ключи и помещает их в массивЗатем основной поток проверяет, сколько ключей было добавлено в массив.
Java 7 типичный вывод (итерация сразу завершается неудачей):
java.util.ConcurrentModificationException
MAX я = 0
Java 6 типичный вывод (вся итерация проходит, и массив содержит все добавленные ключи):
Макс я = 99
Используемый код:
public class Test1 {
public static void main(String[] args) throws InterruptedException {
final int SIZE = 100;
final Map map = new HashMap();
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
final int[] list = new int[SIZE];
final CountDownLatch start = new CountDownLatch(1);
Runnable put = new Runnable() {
@Override
public void run() {
try {
start.await();
for (int i = 4; i < SIZE; i++) {
map.put(i, i);
}
} catch (Exception ex) {
}
}
};
Runnable iterate = new Runnable() {
@Override
public void run() {
try {
start.await();
int i = 0;
for (Map.Entry e : map.entrySet()) {
list[i++] = e.getKey();
Thread.sleep(1);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
ExecutorService e = Executors.newFixedThreadPool(2);
e.submit(put);
e.submit(iterate);
e.shutdown();
start.countDown();
Thread.sleep(100);
for (int i = 0; i < SIZE; i++) {
if (list[i] == 0) {
System.out.println("MAX i = " + i);
break;
}
}
}
}
Примечание: использование JDK 7u11 и JDK 6u38 (64-битная версия) на компьютере с архитектурой x86.