27 Stimmen

Der beste Weg, den gleichzeitigen Zugriff auf Java-Sammlungen zu kontrollieren

Sollte ich alte synchronisierte Vector-Auflistung, ArrayList mit synchronisierten Zugriff oder Collections.synchronizedList oder eine andere Lösung für gleichzeitigen Zugriff verwenden?

Ich sehe meine Frage weder in den verwandten Fragen noch in meiner Suche ( Machen Sie Ihre Sammlungen thread-sicher? ist nicht dasselbe).

Kürzlich musste ich eine Art von Unit-Tests für die GUI-Teile unserer Anwendung durchführen (im Wesentlichen unter Verwendung der API zum Erstellen von Rahmen, Hinzufügen von Objekten usw.). Da diese Operationen viel schneller als von einem Benutzer aufgerufen werden, zeigte es eine Reihe von Problemen mit Methoden, die versuchen, auf noch nicht erstellte oder bereits gelöschte Ressourcen zuzugreifen.

Ein besonderes Problem, das im EDT auftrat, ergab sich aus dem Durchlaufen einer verknüpften Liste von Ansichten, während sie in einem anderen Thread geändert wurde (was neben anderen Problemen zu einer ConcurrentModificationException führte). Fragen Sie mich nicht, warum es eine verknüpfte Liste anstelle einer einfachen Array-Liste war (noch weniger als wir in der Regel 0 oder 1 Ansicht innerhalb haben...), so nahm ich die häufigere ArrayList in meiner Frage (wie es einen älteren Cousin hat).

Wie auch immer, nicht super vertraut mit Gleichzeitigkeit Fragen, sah ich ein bisschen Info, und fragte sich, was zwischen dem alten (und wahrscheinlich veraltet) Vector (die synchronisierte Operationen durch Design hat), ArrayList mit einem synchronized (myList) { } um kritische Abschnitte herum (Hinzufügen/Entfernen/Gehen-Operationen) oder unter Verwendung einer Liste, die von Collections.synchronizedList zurückgegeben wird (ich bin nicht einmal sicher, wie man Letzteres verwendet).

Ich entschied mich schließlich für die zweite Option, da ein weiterer Designfehler darin bestand, das Objekt (getViewList()-Methode...) offenzulegen, anstatt Mechanismen zu seiner Verwendung bereitzustellen.

Aber was sind die Vor- und Nachteile der anderen Ansätze?


[EDIT] Viele gute Ratschläge hier, schwer, einen auszuwählen. Ich werde mich für den ausführlicheren entscheiden, der Links und Denkanstöße enthält... :-) Der von Darron gefällt mir auch.

Zusammengefasst:

  • Wie ich vermutet habe, ist Vector (und sein böser Zwilling, Hashtable wahrscheinlich auch) weitgehend veraltet, ich habe Leute gesehen, die sagen, dass sein altes Design nicht so gut ist wie die neueren Sammlungen, abgesehen von der Langsamkeit der Synchronisation, die sogar in einer Single-Thread-Umgebung erzwungen wird. Wenn wir es behalten, ist es vor allem, weil ältere Bibliotheken (und Teile der Java-API) es noch verwenden.
  • Im Gegensatz zu dem, was ich dachte, Collections.synchronizedXxxx sind nicht moderner als Vector (sie scheinen zeitgenössisch zu Collections, dh. Java 1.2!) und nicht besser, eigentlich. Gut zu wissen. Kurz gesagt, ich sollte sie auch vermeiden.
  • Die manuelle Synchronisierung scheint also doch eine gute Lösung zu sein. Es könnte Leistungsprobleme geben, aber in meinem Fall ist es nicht kritisch: Operationen auf Benutzeraktionen, kleine Sammlung, keine häufige Verwendung.
  • java.util.concurrent-Paket, insbesondere die CopyOnWrite-Methoden, sollten Sie im Auge behalten.

Ich hoffe, ich habe es richtig gemacht... :-)

13voto

Darron Punkte 20861

Ich empfehle dringend das Buch " Java-Gleichzeitigkeit in der Praxis ".

Jede der Möglichkeiten hat Vor- und Nachteile:

  1. Vector - gilt als "veraltet". Sie erhält möglicherweise weniger Aufmerksamkeit und Fehlerkorrekturen als gängigere Sammlungen.
  2. Eigene Synchronisationsblöcke - Sehr leicht zu verwechseln. Ergibt oft eine schlechtere Leistung als die nachstehenden Möglichkeiten.
  3. Collections.synchronizedList() - Wahl 2 wurde von Experten getroffen. Dies ist immer noch nicht vollständig, da es sich um mehrstufige Operationen handelt, die atomar sein müssen (get/modify/set oder Iteration).
  4. Neue Klassen aus java.util.concurrent - Haben oft effizientere Algorithmen als Wahl 3. Es gelten ähnliche Vorbehalte gegen mehrstufige Operationen, aber es werden oft Hilfsmittel zur Verfügung gestellt.

10voto

jcrossley3 Punkte 11196

Ich kann mir keinen guten Grund vorstellen, warum ich jemals die Vector en ArrayList . Listenoperationen auf einer Vector synchronisiert sind, was bedeutet, dass mehrere Threads sie sicher ändern können. Und wie Sie sagen, können die Operationen von ArrayList synchronisiert werden mit Collections.synchronizedList .

Denken Sie daran, dass Sie auch bei der Verwendung von synchronisierten Listen immer noch auf ConcurrentModificationExceptions wenn über die Sammlung iteriert wird, während sie von einem anderen Thread geändert wird. Daher ist es wichtig, den Zugriff von außen zu koordinieren (Ihre zweite Option).

Eine gängige Technik zur Vermeidung des Iterationsproblems besteht darin, über unveränderliche Kopien der Sammlung zu iterieren. Siehe Collections.unmodifiableList

8voto

richs Punkte 4601

Würde ich immer auf die java.util.concurrent ( http://java.sun.com/javase/6/docs/api/java/util/concurrent/package-summary.html ) und sehen Sie nach, ob es eine geeignete Sammlung gibt. Die Klasse java.util.concurrent.CopyOnWriteArrayList ist gut geeignet, wenn Sie nur wenige Änderungen an der Liste vornehmen, aber mehr Iterationen.

Ich glaube auch nicht, dass Vector und Collections.synchronizedList ConcurrentModificationException verhindern.

Wenn Sie keine geeignete Sammlung finden, müssen Sie Ihre eigene Synchronisierung durchführen, und wenn Sie beim Iterieren keine Sperre halten möchten, sollten Sie eine Kopie erstellen und die Kopie iterieren.

3voto

matt b Punkte 135206

Ich glaube nicht, dass die Iterator von einem Vektor zurückgegeben wird, in irgendeiner Weise synchronisiert ist - was bedeutet, dass Vector kann (allein) nicht garantieren, dass ein Thread die zugrunde liegende Sammlung nicht verändert, während ein anderer Thread sie durchläuft.

Ich glaube, dass um sicherzustellen, dass die Iteration Thread-sicher ist, müssen Sie die Synchronisierung auf eigene Faust zu behandeln. Angenommen, dass derselbe Vektor/Liste/Objekt von mehreren Threads geteilt wird (und somit zu Ihren Problemen führt), können Sie nicht einfach auf dieses Objekt selbst synchronisieren?

2voto

Michael Borgwardt Punkte 334642

Die sicherste Lösung ist, den gleichzeitigen Zugriff auf gemeinsame Daten ganz zu vermeiden. Anstatt Nicht-EDT-Threads mit denselben Daten arbeiten zu lassen, sollten sie SwingUtilities.invokeLater() mit einem Runnable, das die Änderungen durchführt.

Im Ernst: Gleichzeitigkeit bei gemeinsam genutzten Daten ist ein Schlangennest, in dem man nie weiß, ob sich nicht irgendwo eine weitere Race Condition oder Deadlock versteckt, die darauf wartet, einem bei der denkbar schlechtesten Gelegenheit in den Hintern zu beißen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X