2015-04-18 21 views
25

Dlaczego ten kod nie rzuca ConcurrentModificationException? Modyfikuje on kod Collection podczas iteracji przez niego, bez użycia metody Iterator.remove(), która ma być the only safe way of removing.Dlaczego ten kod nie generuje wyjątku ConcurrentModificationException?

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C")); 
for (String string : strings) 
    if ("B".equals(string)) 
     strings.remove("B"); 
System.out.println(strings); 

uzyskać ten sam wynik, czy mogę wymienić ArrayList z LinkedList. Jednak jeśli zmienię listę na ("A", "B", "C", "D) lub po prostu ("A", "B") otrzymam wyjątek zgodnie z oczekiwaniami. Co się dzieje? Używam jdk1.8.0_25, jeśli jest to istotne.

EDIT

Znalazłem poniższy link

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4902078

Odpowiednia sekcja jest

Naiwny rozwiązaniem jest dodanie do kontroli komodyfikacja hasNext w AbstractList, ale to podwaja koszty sprawdzania kompryfikacji. Okazuje się, że wystarczy wykonać test tylko w ostatniej iteracji , która praktycznie nie zwiększa kosztów. Innymi słowy, Obecna implementacja hasNext:

public boolean hasNext() { 
     return nextIndex() < size; 
    } 

Czy zastąpione niniejszym realizacji:

public boolean hasNext() { 
     if (cursor != size()) 
      return true; 
     checkForComodification(); 
     return false; 
    } 

Zmiana ta nie zostanie wykonana, ponieważ Słońce wewnętrzny organ regulacyjny odrzucił. Formalne orzeczenie wskazało, że zmiana "ma wartość wykazała potencjał znacznego wpływu na kompatybilność na istniejący kod." („Oddziaływania zgodność” jest to, że poprawka ma potencjał, aby zastąpić cichą wybryków z ConcurrentModificationException.)

+6

Ponieważ 'ConcurrentModificationException' jest wyrzucane na "best-effort" Basis –

+3

Możliwy duplikat: [java.util.ConcurrentModificationException nie wyrzucony, gdy spodziewane] (http://stackoverflow.com/questions/ 24980651/java-util-concurrentmodificationexception-not-throw-when-expected) – Pshemo

+1

Uwielbiam, w jaki sposób Sun nie wprowadził zmiany, może spowodować, że jakiś zły kod zacznie rzucać wyjątek, który miałby wyrzucić – Mshnik

Odpowiedz

21

Zgodnie z ogólną zasadą, ConcurrentModificationException s są wyrzucane gdy modyfikacja jest wykryte, nie spowodowało. Jeśli nigdy nie uzyskasz dostępu do iteratora po modyfikacji, to nie wyrzuci wyjątku. Ten najmniejszy szczegół sprawia, że ​​ConcurrentModificationException jest raczej niewiarygodny w wykrywaniu nadużywania struktur danych, niestety, ponieważ są one wyrzucane dopiero po zniszczeniu.

Ten scenariusz nie rzucać ConcurrentModificationException ponieważ next() nie sprawdzony na stworzonej iterator po modyfikacji.

for-each pętle są naprawdę iteratory, więc kod faktycznie wygląda następująco:

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C")); 
Iterator<String> iter = strings.iterator(); 
while(iter.hasNext()){ 
    String string = iter.next(); 
    if ("B".equals(string)) 
     strings.remove("B"); 
} 
System.out.println(strings); 

Rozważmy kod uruchomiony na liście podany.Iteracji wyglądać następująco:

  1. hasNext() zwraca true, wprowadź pętli, -> ITER przesuwa się indeksem 0, string = "A", nie usunięte
  2. hasNext() powraca prawda nadal pętlę -> ITER przenosi się do indeksu 1 , ciąg = "B", usunięty. strings ma teraz długość 2.
  3. hasNext() zwraca wartość false (iter jest aktualnie na ostatnim indeksie, brak indeksów do przejścia), pętla wyjściowa.

Zatem, ConcurrentModificationException y są generowane, kiedy wywołanie next() wykrywa, że ​​dokonano modyfikacji, scenariusz ten włos unika taki wyjątek.

W przypadku pozostałych dwóch wyników otrzymujemy wyjątki. Dla "A", "B", "C", "D", po usunięciu „B” nadal jesteśmy w pętli, a next() wykrywa ConcurrentModificationException, natomiast dla "A", "B" bym sobie wyobrazić, że to jakiś rodzaj ArrayIndexOutOfBounds który jest złowieniu i ponownie rzucony jako ConcurrentModificationException

+0

Ale na pewno zostałby rzucony, gdyby właśnie sprawdzili, czy nie ma współbieżnej modyfikacji w 'hasNext()' zamiast 'next()'? Jak to jest "najlepszy wysiłek"? –

+0

@pbabcdefp To "best-effort", ponieważ nie ma gwarancji, że JRE zauważy modyfikację współbieżną, jeśli ta modyfikacja nie jest zsynchronizowana. –

+5

@pbabcdefp: ["Best-effort basis"] (http://en.wikipedia.org/wiki/Best-effort_delivery) nie oznacza "spróbujemy i spróbujemy zrobić absolutnie wszystko, co w naszej mocy", np. możesz pomyśleć. Oznacza to, że spróbują, ale nie obiecują. – user2357112

9

hasNext w iterator ArrayList jest tylko

public boolean hasNext() { 
    return cursor != size; 
} 

Po wywołaniu remove, iterator jest w indeksie 2, a lista jest wielkość wynosi 2, a więc informuje, że iteracja jest kompletna. Brak równoczesnej kontroli modyfikacji. W przypadku ("A", "B", "C", "D) lub (" A "," B ") iterator nie znajduje się na nowym końcu listy, dlatego jest wywoływany next i powoduje zgłoszenie wyjątku .

ConcurrentModificationException s to tylko pomoc debugowania. nie można na nich polegać.

1

@Tavian Barnes jest dokładnie prawo. wyjątek ten nie może być zagwarantowane mają być wyrzucane jeżeli równoczesna zmiana w pytaniu nie jest zsynchronizowana. Cytując z java.util.ConcurrentModification specyfikacja:

Zauważ, że nie-szybki zachowanie nie może być zagwarantowane, jak to jest, ogólnie rzecz biorąc , impossibl e do wszelkich twardych gwarancji w obecności niezsynchronizowanej równoczesnej modyfikacji. Operacje Fail-szybko rzucają ConcurrentModificationException na zasadzie najlepszego wysiłku. Dlatego też błędem byłoby napisanie programu, który zależałby od tego wyjątku dla jego poprawności: ConcurrentModificationException powinien być używany tylko do wykrywania błędów.

Link to JavaDoc for ConcurrentModificationException

+0

Należy zauważyć, że przykładowy kod w oryginalnym wpisie jest pojedynczym wątkiem. Tu nie ma problemu z synchronizacją. "Współbieżność" w 'ConcurrentModificationException' nie ma nic wspólnego (bezpośrednio) z wątkami; oznacza to po prostu "w tym samym czasie, kiedy trwała iteracja", a nie "inny wątek zmodyfikował listę". –

Powiązane problemy