167 Stimmen

Ist es sicher, Werte aus einer java.util.HashMap von mehreren Threads (keine Änderung) zu erhalten?

Es gibt einen Fall, in dem eine Karte erstellt wird, die nach ihrer Initialisierung nie wieder geändert wird. Es wird jedoch (nur über get(key)) von mehreren Threads aus auf sie zugegriffen werden. Ist es sicher, eine java.util.HashMap auf diese Weise?

(Derzeit verwende ich zum Glück eine java.util.concurrent.ConcurrentHashMap und habe keinen gemessenen Bedarf, die Leistung zu verbessern, sondern bin einfach neugierig, ob eine einfache HashMap ausreichen würde. Daher ist diese Frage no Es geht weder um die Frage "Welches soll ich verwenden?" noch um die Leistung. Die Frage lautet vielmehr: "Wäre es sicher?")

4 Stimmen

Viele Antworten hier sind richtig in Bezug auf den gegenseitigen Ausschluss von laufenden Threads, aber falsch in Bezug auf Speicheraktualisierungen. Ich habe entsprechend hoch/runter gestimmt, aber es gibt immer noch viele falsche Antworten mit positiven Stimmen.

0 Stimmen

@Heath Borders, wenn die Instanz a statisch initialisiert wurde unmodifiable HashMap, sollte es sicher für gleichzeitiges Lesen sein (wie andere Threads nicht Updates verpasst haben könnte, da es keine Updates), richtig?

0 Stimmen

Wenn es statisch initialisiert und nie außerhalb des statischen Blocks geändert wird, dann könnte es in Ordnung sein, weil alle statischen Initialisierungen durch die Synchronisierung der ClassLoader . Das ist schon eine eigene Frage wert. Ich würde es trotzdem explizit synchronisieren und ein Profil erstellen, um zu überprüfen, ob es wirklich Leistungsprobleme verursacht.

73voto

Taylor Gautier Punkte 4828

Jeremy Manson, der Gott, wenn es um das Java-Speichermodell geht, hat einen dreiteiligen Blog zu diesem Thema - denn im Wesentlichen stellen Sie die Frage "Ist es sicher, auf eine unveränderliche HashMap zuzugreifen" - die Antwort darauf ist ja. Aber Sie müssen das Prädikat dieser Frage beantworten: "Ist meine HashMap unveränderlich". Die Antwort wird Sie vielleicht überraschen - Java hat ein relativ kompliziertes Regelwerk zur Bestimmung der Unveränderlichkeit.

Weitere Informationen zu diesem Thema finden Sie in Jeremy's Blogbeiträgen:

Teil 1 über Unveränderlichkeit in Java: http://jeremymanson.blogspot.com/2008/04/immutability-in-java.html

Teil 2 über Unveränderlichkeit in Java: http://jeremymanson.blogspot.com/2008/07/immutability-in-java-part-2.html

Teil 3 über Unveränderlichkeit in Java: http://jeremymanson.blogspot.com/2008/07/immutability-in-java-part-3.html

3 Stimmen

Es ist ein guter Punkt, aber ich verlasse mich auf statische Initialisierung, während der keine Verweise zu entkommen, so sollte es sicher sein.

12 Stimmen

Ich verstehe nicht, wie dies eine hoch bewertete Antwort (oder überhaupt eine Antwort) sein kann. Sie enthält zum einen nicht einmal Antwort die Frage, und es wird nicht das eine Schlüsselprinzip erwähnt, das darüber entscheidet, ob es sicher ist oder nicht: sichere Veröffentlichung . Die "Antwort" läuft darauf hinaus, dass es "trickreich" ist, und hier sind drei (komplexe) Links, die Sie lesen können.

0 Stimmen

Er beantwortet die Frage ganz am Ende des ersten Satzes. Was die Antwort betrifft, so weist er darauf hin, dass die Unveränderlichkeit (auf die im ersten Absatz der Frage angespielt wird) nicht einfach ist, und nennt wertvolle Quellen, die dieses Thema näher erläutern. Die Punkte messen nicht, ob es eine Antwort ist, sondern ob die Antwort für andere "nützlich" war. Dass die Antwort akzeptiert wurde, bedeutet, dass sie die Antwort war, nach der der Fragesteller gesucht hat, und die Ihre Antwort erhalten hat.

38voto

Heath Borders Punkte 29263

Die Lesevorgänge sind aus der Sicht der Synchronisierung sicher, aber nicht aus der Sicht des Speichers. Dies ist etwas, das unter Java-Entwicklern weithin missverstanden wird, auch hier auf Stackoverflow. (Beachten Sie die Bewertung von diese Antwort zum Beweis).

Wenn Sie andere Threads laufen haben, sehen diese möglicherweise keine aktualisierte Kopie der HashMap, wenn der aktuelle Thread keinen Speicher schreibt. Das Schreiben in den Speicher erfolgt durch die Verwendung der Schlüsselwörter synchronized oder volatile oder durch die Verwendung einiger Java-Concurrency-Konstrukte.

Voir Der Artikel von Brian Goetz über das neue Java-Speichermodell für Details.

0 Stimmen

Entschuldigung für die doppelte Einreichung Heath, ich habe deine erst bemerkt, nachdem ich meine eingereicht hatte :)

2 Stimmen

Ich bin nur froh, dass es hier noch andere Menschen gibt, die die Auswirkungen des Gedächtnisses wirklich verstehen.

1 Stimmen

In der Tat, obwohl kein Thread das Objekt sehen wird, bevor es richtig initialisiert ist, also denke ich nicht, dass das in diesem Fall ein Problem ist.

11voto

Dave L. Punkte 42559

Nach längerem Suchen fand ich dies in der Java-Doku (Hervorhebung von mir):

Beachten Sie, dass diese Implementierung nicht synchronisiert. Wenn mehrere Threads gleichzeitig auf eine Hash-Map zugreifen und mindestens mindestens einer der Threads die Map strukturell verändert, muss sie extern synchronisiert werden. (Eine strukturelle Änderung ist jede Operation, die eine oder mehrere Zuordnungen hinzufügt oder löscht; das bloße Ändern des Wertes, der mit einem mit einem Schlüssel, den eine Instanz bereits enthält, ist keine strukturelle Modifikation).

Dies scheint zu bedeuten, dass es sicher ist, vorausgesetzt, die Umkehrung der dortigen Aussage ist wahr.

1 Stimmen

Dies ist zwar ein ausgezeichneter Ratschlag, wie andere Antworten zeigen, aber im Falle einer unveränderlichen, sicher veröffentlichten Map-Instanz gibt es eine differenziertere Antwort. Aber das sollten Sie nur tun, wenn Sie wissen, was Sie tun.

1 Stimmen

Mit Fragen wie diesen können hoffentlich mehr von uns wissen, was wir tun.

1 Stimmen

Das ist nicht ganz richtig. Wie die anderen Antworten zeigen, muss es eine passiert-vor zwischen der letzten Änderung und allen nachfolgenden "threadsicheren" Lesevorgängen. Normalerweise bedeutet dies, dass Sie das Objekt sicher veröffentlichen müssen nach er erstellt wurde und seine Änderungen vorgenommen werden. Siehe die erste, als richtig markierte Antwort.

8voto

Alex Miller Punkte 67243

Ein Hinweis ist, dass unter bestimmten Umständen ein get() aus einer unsynchronisierten HashMap eine Endlosschleife verursachen kann. Dies kann passieren, wenn ein gleichzeitiges put() einen Rehash der Map verursacht.

http://lightbody.net/blog/2005/07/hashmapget_can_cause_an_infini.html

1 Stimmen

Tatsächlich habe ich gesehen, dass dies die JVM hängen, ohne CPU zu verbrauchen (was vielleicht schlimmer ist)

2 Stimmen

I piense en dieser Code wurde so umgeschrieben, dass es nicht mehr möglich ist, die Endlosschleife zu erhalten. Aber Sie sollten immer noch nicht schlagen immer und Putting aus einer unsynchronisierten HashMap aus anderen Gründen.

0 Stimmen

@AlexMiller auch abgesehen von den anderen Gründen (ich nehme an, Sie beziehen sich auf die sichere Veröffentlichung), denke ich nicht, dass eine Implementierungsänderung ein Grund sein sollte, die Zugangsbeschränkungen zu lockern, es sei denn, die Dokumentation lässt dies ausdrücklich zu. Zufälligerweise ist die HashMap Javadoc für Java 8 enthält immer noch diese Warnung: Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.

7voto

Alexander Punkte 8932

Es gibt jedoch eine wichtige Wendung. Es ist sicher, auf die Map zuzugreifen, aber es ist im Allgemeinen nicht garantiert, dass alle Threads genau den gleichen Zustand (und damit die gleichen Werte) der HashMap sehen werden. Dies kann auf Multiprozessorsystemen der Fall sein, wo die Änderungen an der HashMap, die von einem Thread (z. B. dem, der sie gefüllt hat) vorgenommen wurden, im Cache dieser CPU liegen können und von Threads, die auf anderen CPUs laufen, nicht gesehen werden, bis eine Memory-Fence-Operation durchgeführt wird, die die Cache-Kohärenz sicherstellt. Die Java Sprachspezifikation ist in diesem Punkt eindeutig: Die Lösung besteht darin, eine Sperre (synchronisiert (...)) zu erwerben, die eine Memory-Fence-Operation auslöst. Wenn Sie also sicher sind, dass nach dem Auffüllen der HashMap jeder der Threads JEDE Sperre erwirbt, dann ist es von diesem Zeitpunkt an OK, von jedem Thread auf die HashMap zuzugreifen, bis die HashMap wieder geändert wird.

0 Stimmen

Ich bin nicht sicher, dass der Thread, der darauf zugreift, eine Sperre erhält, aber ich bin sicher, dass er keinen Verweis auf das Objekt erhält, bis es initialisiert wurde, also glaube ich nicht, dass er eine veraltete Kopie haben kann.

0 Stimmen

@Alex: Der Verweis auf die HashMap kann flüchtig sein, um die gleiche Speichersichtbarkeit zu gewährleisten. @Dave: Es ist möglich, Verweise auf neue Objekte zu sehen, bevor die Arbeit des Ctors für Ihren Thread sichtbar wird.

0 Stimmen

@Christian Im allgemeinen Fall, sicherlich. Ich wollte damit sagen, dass es in diesem Code nicht der Fall ist.

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