Frage

Ich verstehe nicht, warum ich eine ConcurrentModificationException bekomme, wenn ich durch das iteriere multimap. Ich habe Folgendes gelesen Eintrag, aber ich bin mir nicht sicher, ob ich das Ganze verstanden habe. Ich habe versucht, einen synchronisierten Block hinzuzufügen. Aber mein Zweifel ist, was ich synchronisieren kann und wann.

Das multimap ist ein Feld und so erstellt:

private Multimap<GenericEvent, Command> eventMultiMap =   
   Multimaps.synchronizedMultimap(HashMultimap.<GenericEvent, Command> create());

und so verwendet:

eventMultiMap.put(event, command);

Und so (ich habe versucht, diesen Teil auf der Karte zu synchronisieren, aber ohne Erfolg)

for (Entry<GenericEvent, Command> entry : eventMultiMap.entries()) {
    if (entry.getValue().equals(command)) {
        eventMultiMap.remove(entry.getKey(), entry.getValue());
        nbRemoved++;
    }
}
War es hilfreich?

Lösung

In Java8 können Sie auch einen Lambda -Ansatz verwenden:

eventMultiMap.entries().removeIf(genericEventCommandEntry -> genericEventCommandEntry.getValue().equals(command));

Andere Tipps

Das Aufrufen einer Sammlung, während Sie sie durch sie durchführen, verursachen jedes Mal eine ConcurrentModificificationException, auch wenn alles im selben Thread erledigt ist - das Richtige ist, einen expliziten Iterator zu erhalten und.

Bearbeiten: Ändern Ihres Beispiels:

Iterator<Map.Entry<GenericEvent, Command>> i = eventMultiMap.entries().iterator();
while (i.hasNext()) {
    if (i.next().getValue().equals(command)) {
        i.remove();
        nbRemoved++;
    }
}

Sie möchten vielleicht sehen Dieser Blogpost Für einen weiteren Fall, der a ergibt ConcurrentModificationException Beim Überqueren eines Multimaps, ohne dass sich kein anderer Faden stört. Kurz gesagt, wenn Sie die Schlüssel von Multimap durchqueren, auf die jeweilige Sammlung von Werten zugreifen, die jedem Schlüssel zugeordnet sind, und entfernen Sie einige Elemente aus einer solchen Sammlung. Wenn dieses Element der letzte der Sammlung ist du wirst haben ConcurrentModificationException Wenn Sie versuchen, auf den nächsten Schlüssel zuzugreifen - weil das Entleeren einer Sammlung die Entfernung des Schlüssels auslöst und so den Schlüsselset des Multimap -Tastens strukturell modifiziert.

Wenn ein anderer Thread Ihr Multimap ändern könnte, während diese Logik ausgeführt wird, müssen Sie dem Mharris -Code einen synchronisierten Block hinzufügen:

synchronized (eventMultimap) {
  Iterator<Entry<GenericEvent, Command>> i = eventMultiMap.entries.iterator();
  while (i.hasNext()) {
    if (i.next().getValue().equals(command)) {
        i.remove();
        nbRemoved++;
    }
  }
}

Oder Sie können den Iterator wie folgt weglassen,

synchronized (eventMultimap) {
  int oldSize = eventMultimap.size();
  eventMultimap.values().removeAll(Collections.singleton(command));
  nbRemoved = oldSize - eventMultimap.size();
}

Der Removeall () -Anruf erfordert keine Synchronisation. Wenn Sie jedoch den synchronisierten Block weglassen, könnte das Multimap zwischen dem removeall () -Anruf und einem der Größe () () mutieren, was zu einem falschen Wert von NBREMOPED führt.

Wenn Ihr Code nun Single-Threaded ist und Sie nur einen Aufruf von ConcurrentModificificationException vermeiden möchten, können Sie die Multimaps herauslassen.

ich bevorzuge Multimap.values().iterator() Wenn Sie sich nicht um den Schlüssel kümmern. Sie sollten auch versuchen, sich so weit wie möglich von synchronisierten Blöcken fernzuhalten, da Sie Lese-/Schreibvorgänge nicht effektiv priorisieren können.

ReadWriteLock lock = new ReentrantReadWriteLock();
Lock writeLock = lock.writeLock(); 

public void removeCommands(Command value) {
  try {
    writeLock.lock();
    for (Iterator<Command> it = multiMap.values().iterator(); it.hasNext();) {
      if (it.next() == value) {
        it.remove();
      }
    }
  } finally {
    writeLock.unlock();
  }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top