Frage

In einer Multithread-Anwendung arbeite ich an, wir sehen ConcurrentModificationExceptions auf unseren Listen gelegentlich (die meist ArrayList sind, manchmal Vektoren). Aber es gibt andere Zeiten, in denen ich denke, gleichzeitige Änderungen geschehen, weil durch die Sammlung Iterieren von Elementen angezeigt wird, zu fehlen, aber keine Ausnahmen ausgelöst. Ich weiß, dass die Dokumentation für ConcurrentModificationException sagt man sich nicht darauf verlassen kann, aber wie würde ich mich darum, dass ich nicht gleichzeitig eine Liste zu modifizieren? Und wird jeden Zugriff auf die Sammlung in einem synchronisierten Block den einzigen Weg, Einwickeln es zu verhindern?

Update: Ja, ich weiß über Collections.synchronizedCollection, aber es funktioniert nicht schützen gegen jemand die Sammlung zu modifizieren, während Sie durch sie sind laufen. Ich denke, dass zumindest ein Teil meines Problems geschieht, wenn jemand etwas zu einer Sammlung hinzufügt, während ich durch sie bin iterieren.

Zweites Update Wenn jemand die Erwähnung des SynchronizedCollection verbinden möchte und Klonen wie Jason hat mit einer Erwähnung des java.util.concurrent und die Apache-Sammlungen Frameworks wie jacekfoo und Javamann tat, kann ich akzeptiert eine Antwort.

War es hilfreich?

Lösung

Ihre ursprüngliche Frage scheint für einen Iterator zu fragen, das Live-Updates auf die zugrunde liegende Auflistung sieht, während verbleibenden Thread-sicher. Dies ist ein unglaublich teures Problem im allgemeinen Fall zu lösen, weshalb keine der Standard-Collection-Klassen es tun.

Es gibt viele Wege zur Erreichung Teillösungen für das Problem, und in der Anwendung, einer von denen, ausreichend sein.

Jason gibt Thread-Sicherheit zu erreichen, und ein ConcurrentModificationException zu vermeiden, werfen, aber nur auf Kosten der Lebendigkeit.

Javamann erwähnt zwei spezielle Klassen in dem java.util.concurrent Paket, das das gleiche Problem in einem Schloss zu lösen -freie Weise, in der Skalierbarkeit entscheidend ist. Diese werden nur versandt mit Java 5, aber es werden verschiedene Projekte gewesen, die die Funktionalität des Pakets in frühere Java-Versionen zurückzuportieren, einschließlich dieses , obwohl sie so gute Leistung nicht in früheren JREs haben.

Wenn Sie bereits einige der Apache Commons Bibliotheken verwenden, dann als jacekfoo weist darauf hin, , apache Sammlungen Rahmen einige hilfreiche Klassen enthält.

Sie könnten auch erwägen im Google Sammlungen Rahmen .

Andere Tipps

Je nach Update-Frequenz einer meiner Favoriten ist der CopyOnWriteArrayList oder CopyOnWriteArraySet. Sie schaffen eine neue Liste / set auf Updates gleichzeitige Änderung Ausnahme zu vermeiden.

Schauen Sie sich java.util.concurrent für Versionen der Standardsammlungen Klassen, die besser in der Griff Gleichzeitigkeit entwickelt werden.

Ja haben Sie den Zugriff auf Sammlungen Objekte zu synchronisieren.

Alternativ können Sie die synchronisierten Wrapper um jedes vorhandene Objekt verwenden. Siehe Kollektionen. SynchronizedCollection () . Zum Beispiel:

List<String> safeList = Collections.synchronizedList( originalList );

Doch alle Code muss die sichere Version verwenden, und auch so Iterieren während ein anderer Thread modifiziert zu Problemen führen wird.

die Iteration Problem zu lösen, zuerst die Liste kopieren. Beispiel:

for ( String el : safeList.clone() )
{ ... }

Für weitere optimierte, Thread-sichere Sammlung, auch einen Blick auf java.util.concurrent .

Sie in der Regel eine ConcurrentModificationException erhalten, wenn Sie versuchen, ein Element aus einer Liste zu entfernen, während es durch iteriert ist wird.

Der einfachste Weg, dies zu testen ist:

List<Blah> list = new ArrayList<Blah>();
for (Blah blah : list) {
     list.remove(blah); // will throw the exception
}

Ich bin mir nicht sicher, wie man es umgehen würde. Sie können Ihre eigene Thread-sichere Liste haben zu implementieren, oder Sie können Kopien der ursprünglichen Liste zum Schreiben erstellen und haben eine synchronisierte Klasse, die in die Liste schreibt.

Sie könnten versuchen, defensives Kopieren verwenden, so dass Änderungen an einem List andere nicht beeinflussen.

Wrapping der Sammlung greift in einem synchronisierten Block ist der richtige Weg, dies zu tun. Standardprogrammier diktiert die Verwendung einer Art von Verriegelungsmechanismus (Semaphore, Mutex, usw.), wenn sie mit Zustand tun haben, die über mehrere Threads gemeinsam genutzt wird.

auf Ihrem Anwendungsfall Je jedoch können Sie in der Regel einige Optimierungen nur Sperre in bestimmten Fällen machen. Zum Beispiel, wenn Sie eine Sammlung, die häufig gelesen wird, aber selten geschrieben, dann können Sie erlauben die gleichzeitige liest aber eine Sperre erzwingen, wenn ein Schreib im Gang ist. Concurrent liest nur zu Konflikten führen, wenn die Sammlung in den Prozess der modifiziert werden.

ConcurrentModificationException ist best-effort, weil, was Sie fordern ein schwieriges Problem ist. Es gibt keine gute Möglichkeit, dies zuverlässig zu tun, ohne Leistung zu opfern neben beweisen, dass Ihre Zugriffsmuster gleichzeitig die Liste nicht ändern.

Synchronisation verhindern würde wahrscheinlich gleichzeitige Änderungen, und es kann sein, was Sie am Ende zurückgreifen, aber es kann sein teuer enden. Das Beste, was zu tun ist wahrscheinlich, sich zu setzen und für eine Weile über Ihre Algorithmus zu denken. Wenn Sie nicht mit einer Lock-freier Lösung kommen können, dann auf der Synchronisation zurückgreifen.

Sehen Sie die Implementierung. Es speichert im Grunde ein int:

transient volatile int modCount;

und dass inkrementiert wird, wenn es eine ‚strukturelle Modifikation‘ ist (wie entfernen). Wenn Iterator erkennt, dass modCount geändert wirft es Concurrent Änderung Ausnahme.

Synchronisieren (über Collections.synchronizedXXX) nicht gut tun, da es sich nicht Iterator Sicherheit garantiert nur synchronisiert schreiben und lesen über put, get, set ...

Siehe java.util.concurennt und Apache Sammlungen Rahmen (es einige Klassen hat, die optimiert werden korrekt in Concurrent-Umgebung funktionieren, wenn es mehr liest (die nicht synchronisiert sind) als Schreib - vgl. FastHashMap

Sie können auch über iteratins über die Liste synchronisieren.

List<String> safeList = Collections.synchronizedList( originalList );

public void doSomething() {
   synchronized(safeList){
     for(String s : safeList){
           System.out.println(s);

     }
   }

}

Damit wird die Liste auf Synchronisation sperren und alle Threads blockieren, die versuchen, die Liste zuzugreifen, während Sie es über sie bearbeiten oder iterieren. Der Nachteil ist, dass Sie einen Engpass erstellen.

Das spart etwas Speicher über die .clone () -Methode und könnte schneller sein, je nachdem, was Sie in der Iteration tun ...

Collections.synchronizedList () eine Liste machen nominell Thread-sicher und java.util.concurrent enthält leistungsfähigere Funktionen.

Dies wird Ihre gleichzeitigen Änderung Ausnahme loszuwerden. Ich werde jedoch nicht auf die Effizienz sprechen;)

List<Blah> list = fillMyList();
List<Blah> temp = new ArrayList<Blah>();
for (Blah blah : list) {
     //list.remove(blah);  would throw the exception
     temp.add(blah);
}
list.removeAll(temp);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top