2017-05-18 7 views
2

Ich habe eine ArrayList mit zwei Accessor-Methoden und einem Notifier. Meine Liste:ArrayList Iterator werfen ConcurrentModificationException

private final List<WeakReference<LockListener>> listeners = new ArrayList<>(); 

Alle abonnieren Operationen verwenden:

public void subscribe(@NonNull LockListener listener) { 
    for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext();) { 
     // has this one already subscribed? 
     if (listener.equals(it.next().get())) { 
      return; 
     } 
    } 
    listeners.add(new WeakReference<>(listener)); 
} 

Alle Abmeldevorgänge verwenden diese:

public void unsubscribe(@NonNull LockListener listener) { 
    if (listeners.isEmpty()) { 
     return; 
    } 

    for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext();) { 
     WeakReference<LockListener> ref = it.next(); 
     if (ref == null || ref.get() == null || listener.equals(ref.get())) { 
      it.remove(); 
     } 
    } 
} 

Und den Antragsteller:

private void notifyListeners() { 
    if (listeners.isEmpty()) { 
     return; 
    } 

    Iterator<WeakReference<LockListener>> it = listeners.iterator(); 
    while (it.hasNext()) { 
     WeakReference<LockListener> ref = it.next(); 
     if (ref == null || ref.get() == null) { 
      it.remove(); 
     } else { 
      ref.get().onLocked(); 
     } 
    } 
} 

Was ich Ich sehe in meiner Prüfung, dass es.next() in n otifyListeners() löst gelegentlich eine ConcurrentModificationException aus. Meine Vermutung ist, dass dies auf listeners.add() in der Teilnehmermethode zurückzuführen ist.

Ich denke, ich hatte hier ein Missverständnis des Iterators. Ich ging davon aus, dass das Iterieren über die Liste mich vor Nebenläufigkeitsproblemen schützte, die durch Add/Remove-Vorgänge verursacht wurden.

Anscheinend liege ich hier falsch. Ist der Iterator nur ein Schutz vor der ConcurrentModificationException beim Ändern der Sammlung, die Sie gerade durchlaufen? Wenn Sie z. B. während der Iteration remove() auf Ihrer Liste aufrufen, wird zwar ein Fehler ausgegeben, der Aufruf von it.remove() ist jedoch sicher.

In meinem Fall, subskribieren Anrufe() auf der gleichen Liste wie es iteriert wird. Ist mein Verständnis hier korrekt?

+0

Wenn Sie die Dokumentation für Iterator lesen, wird Ihnen mitgeteilt, dass Sie die zugrunde liegende Struktur nicht ändern können – efekctive

Antwort

2

Wenn ich Ihren letzten Satz richtig gelesen habe, werden die drei Methoden in Ihrem Beispiel gleichzeitig von mehreren Threads aufgerufen. Wenn das tatsächlich der Fall ist, dann ist das dein Problem.

ArrayList ist nicht Thread-Safe. Eine gleichzeitige Änderung ohne zusätzliche Synchronisation führt zu undefiniertem Verhalten, unabhängig davon, ob Sie es direkt ändern oder einen Iterator verwenden.

Sie können entweder den Zugriff auf die Liste synchronisieren (z. B. das Synchronisieren der drei Methoden) oder eine Thread-sichere Erfassungsklasse wie ConcurrentLinkedDeque verwenden. Im letzteren Fall sollten Sie JavaDoc lesen (insbesondere den Teil über Iteratoren, der wöchentlich konsistent ist), um zu verstehen, was garantiert ist und was nicht.

Verwandte Themen