Не могу понять этот комментарий о отказоустойчивости

В HashSet.java в JDK 1.6 есть некоторые комментарии о свойстве отказоустойчивости итератора HashSet.

Итераторы, возвращаемые методом итератора этого класса, являются отказоустойчивыми: если набор изменяется в любое время после создания итератора любым способом, кроме как с помощью собственного метода удаления итератора, итератор создает исключение ConcurrentModificationException. Таким образом, перед лицом одновременной модификации итератор быстро и чисто дает сбой, а не рискует произвольным, недетерминированным поведением в неопределенное время в будущем.

Я могу понять приведенный выше абзац, потому что он довольно прост и ясен, но я не могу понять следующий абзац. Я могу понять это, если у меня есть несколько простых примеров, показывающих, что отказоустойчивый итератор может даже дать сбой.

Обратите внимание, что безотказное поведение итератора не может быть гарантировано, так как, вообще говоря, невозможно дать какие-либо жесткие гарантии при наличии несинхронизированной параллельной модификации. Отказоустойчивые итераторы генерируют исключение ConcurrentModificationException в максимально возможной степени. Поэтому было бы неправильно писать программу, корректность которой зависела бы от этого исключения: безотказное поведение итераторов следует использовать только для обнаружения ошибок.


person Heejin    schedule 27.08.2012    source источник
comment
В основном это говорит о том, что не полагайтесь на Java, чтобы определенно сообщить вам, что вы злоупотребляете итераторами. Проще говоря, вы не можете изменить список во время его повторения.   -  person thatidiotguy    schedule 27.08.2012


Ответы (2)


РЕДАКТИРОВАТЬ: Извините, я использовал список, но это та же идея. Речь идет об итераторе, а не о коллекции, стоящей за ним.

РЕДАКТИРОВАТЬ 2: также гораздо чаще это происходит в многопоточной среде, где у вас есть два потока, один для чтения, один для записи. Их труднее увидеть, когда вы кодируете. Чтобы исправить это, вам нужно реализовать блокировки чтения/записи в списке, чтобы избежать этого.

Вот пример кода для комментария:

Iterator itr = myList.iterator();

while(itr.hasNext())
{
    Object o = itr.next();

    if(o meets some condition)
    { 
        //YOURE MODIFYING THE LIST
        myList.remove(o);
    }
}

Спецификации говорят о том, что вы не можете полагаться на такой код:

while(itr.hasNext())
{
     Object o = itr.next();

     try
     {
         if(o meets some condition)
            myList.remove(o);
     }
     catch(ConcurrentModificationException e)
     {
         //Whoops I abused my iterator. Do something else.
     }
}

Вместо этого вам, вероятно, следует добавить что-то в новый список, а затем переключить ссылку myList на только что созданную. Это объясняет ситуацию?

person thatidiotguy    schedule 27.08.2012

Идея второго абзаца состоит в том, чтобы помешать вам написать такой код:

boolean ok = false;
do {
    try {
        doTheModification();
        ok = true;
    } catch {
        // Consurrent modification - retry
    }
} while (!ok);

Хотя это не очень хороший код для начала, в комментарии говорится, что этот код недействителен (в отличие, скажем, от неоптимального). Они говорят, что исключение может вообще не следовать, поэтому цикл, описанный выше, может молча произвести сбой.

person Sergey Kalinichenko    schedule 27.08.2012
comment
Это очень краткий пример того, что вы могли бы написать (наивно и неправильно), если бы у вас было несколько потоков, работающих с одним и тем же списком. Вместо этого вы хотите использовать блокировки чтения/записи языков Java. Не полагаться на ConcurrentModificationException. - person thatidiotguy; 27.08.2012