Почему нет ConcurrentModificationException, когда один поток повторяется (используя Iterator), а другой поток изменяет одну и ту же копию не-поточно-безопасного ArrayList
Немного предыстории:
Когда коллекция повторяется с использованием Iterator
тогда мог java.util.ConcurrentModificationException
потому что под капотами, когда Iterator
объект создается, то счетчик модификаций (modCount
) коллекции или ArrayList захватывается и на каждой итерации, используя Iterator.next()
проверяется, если modCount
изменилось, если так то киньте java.util.ConcurrentModificationException
,
При создании объекта итератора (из ArrayList
"s Iterator
реализация): int expectedModCount = modCount;
Ниже метод вызывается Iterator.next()
, который выдает исключение (из ArrayList
"s Iterator
реализация):
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Я мог бы очень хорошо воспроизвести это, используя следующий код:
List<String> stringList = new ArrayList<String>();
stringList.add("a");
stringList.add("b");
stringList.add("c");
Iterator<String> iterator = stringList.iterator();
System.out.println("Got iterator object");
while (iterator.hasNext()) {
String player = iterator.next();
player.toString();
System.out.println("UpperCase: " + player.toUpperCase());
iterator.remove();
stringList.add("a1"); //This is root cause of exception.
}
Вопрос:
- Если я пытаюсь изменить список, используя другой поток (код ниже), то я не получаю
java.util.ConcurrentModificationException
но если я использую тот же поток, я получаю исключение.
Если я правильно понимаю, то еще на каждом проходе Iterator.next()
из первого потока, Iterator.checkForComodification()
будет вызываться изнутри, и так как мой второй поток изменил список, последний modCount
не будет равных Iterator.expectedModCount
который был инициализирован, когда Iterator
объект был создан, следовательно, исключение должно было произойти из Iterator.checkForComodification()
, Правильно?
Я убедился, что список обновляется из потока 2, а итератор из потока 1 может напечатать новое значение, потому что я вижу напечатанное значение "d", которое добавляется вторым потоком.
private static void iteratorException() {
final List<String> stringList = new ArrayList<String>();
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("T1");
stringList.add("a");
stringList.add("b");
stringList.add("c");
Iterator<String> iterator = stringList.iterator();
System.out.println("Got iterator object");
while (iterator.hasNext()) {
String player = iterator.next();
player.toString();
System.out.println("UpperCase: " + player.toUpperCase());
iterator.remove();
//stringList.add("a1");
}
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
System.out.println("T2");
stringList.add("d");
stringList.remove("a");
System.out.println("finished T2");
}
};
thread.start();
thread2.start();
}