Java: Caching von nicht-volatile-Variablen von verschiedenen threads
Die situation ist folgende:
- Ich habe ein Objekt mit viel setter und Getter.
- Instanz dieses Objekts erstellt wird, in einem bestimmten thread, wo alle Werte eingestellt sind. Zunächst erstelle ich ein "leeres" Objekt mithilfe von new-Anweisung und nur dann rufe ich einige setter-Methoden basiert auf einem komplizierten Erbe, die Logik.
- Nur dann ist dieses Objekt verfügbar geworden ist, auf alle anderen threads, die verwenden nur Getter.
Die Frage: muss ich machen, damit alle Variablen dieser Klasse flüchtigen oder nicht?
Betrifft:
- Erstellung einer neuen Instanz des Objekts und Einstellung alle Werte
ist in der Zeit getrennt. - Aber alle anderen threads, die haben keine Ahnung
neue Instanz, bis alle Werte eingestellt sind. Also in anderen threads ist nicht
ein cache der nicht vollständig initialisierte Objekt. Ist es nicht?
Hinweis: ich bin mir bewusst, über das builder pattern, aber ich kann nicht angewendet werden, gibt es mehrere andere Gründe 🙁
BEARBEITET:
Wie ich finde zwei Antworten von Mathias und axtavt passen nicht sehr gut, ich hinzufügen möchte, ein Beispiel:
Sagen wir, wir haben ein foo
Klasse:
class Foo {
public int x=0;
}
und zwei threads sind, es zu benutzen, wie oben beschrieben:
//Thread 1 init the value:
Foo f = new Foo();
f.x = 5;
values.add(f); //Publication via thread-safe collection like Vector or Collections.synchronizedList(new ArrayList(...)) or ConcurrentHashMap?.
//Thread 2
if (values.size()>0){
System.out.println(values.get(0).x); //always 5 ?
}
Als ich Verstand, Mathias, es kann drucken Sie 0 auf einige JVM nach JLS. Wie ich verstanden habe axtavt es wird immer drucken 5.
Was ist Ihre Meinung?
--
Grüße,
Dmitriy
- Ich kann mich nicht entscheiden, welche Antwort richtig ist. Würde gerne hören, die Meinung der anderen Teilnehmer.
- Die beiden Antworten miteinander vereinbar sind. axtavt ist keine Vermutung darüber, Wann die threads gestartet werden, damit es beschäftigt sich mit dem Allgemeinen Fall. Mathias ist Adressen, was passiert, wenn du weißt, dass dein Objekt aufgebaut ist und dessen Wert gesetzt werden, bevor der thread gestartet wird (was in der axtavt Worte, könnte gesehen werden als ein Spezialfall der sicheren Veröffentlichung, Dank der Sichtbarkeit der Hindernisse erzwungen durch das starten eines Threads ). Sie ergänzen sich gegenseitig (wenn Ihr threads gestartet werden, bevor die Werte gesetzt sind, werden carreful, wo Sie Ihre
volatile
s).
Du musst angemeldet sein, um einen Kommentar abzugeben.
In diesem Fall müssen Sie die Verwendung von die sichere Veröffentlichung Idiome, wenn in Ihrem Objekt zur Verfügung, zu anderen threads, nämlich (von Java Concurrency in Practice):
Wenn Sie die sichere Veröffentlichung, Sie brauchen nicht zu erklären, Felder
volatile
.Jedoch, wenn Sie es nicht verwenden, deklarieren Felder
volatile
(theoretisch) nicht helfen, da die memory-Barrieren entstehen durchvolatile
sind, der auf einer Seite: flüchtige schreiben kann nachbestellt werden, die mit nicht-flüchtigen Aktionen nach.So,
volatile
gewährleistet die Korrektheit der folgende Fall:aber nicht in diesem Fall:
Aus der theoretischen Sicht, in der ersten Probe gibt es eine transitive passiert-vor der Beziehung
Im zweiten Beispiel
f.x = 42
und Lesen vonfoo.x
sind nicht verbunden durch happens-before Beziehung, deshalb können Sie ausgeführt werden, in beliebiger Reihenfolge.Safe publication
scheint abgeleitet werden Verfahren aus der Java-concurrency-Modell in JLS Kapitel 17.foo = new Foo(); foo.x= 42;
get(0)
). Publikation über die thread-sichere collection ist eine sichere Veröffentlichung als auch, so brauchen Sie nichtvolatile
.volatile
2 Felder), ohne die sichere Veröffentlichungvolatile
Felder nicht helfenBrauchen Sie nicht, erklären Sie Feld flüchtigen deren Wert festgelegt ist, bevor die
start
Methode wird aufgerufen, auf die threads, lese-Bereich.Der Grund ist, dass in diesem Fall die Einstellung ist in einem passiert-vor relation (wie definiert in the Java Language Specification) mit dem Lesen im anderen thread.
Den einschlägigen Regeln der JLS sind:
Aber, wenn Sie beginnen die anderen threads, bevor Sie das Feld, dann müssen Sie erklären Bereich volatile. Die JLS nicht zulassen, dass Sie davon ausgehen, dass der thread nicht in die Caches der Wert, bevor er liest es zum ersten mal, auch wenn dass der Fall sein kann, auf eine bestimmte version der JVM.
volatile
macht in diesem Falle keinen Sinn und wird nicht helfen, seit der Veröffentlichung des Objekts neu angeordnet werden können und mit flüchtigen schreibt von seinen Feldern.volatile
modifier, die ich nicht identifizieren kann, eine happens-before Beziehung zwischen dem schreiben und dem Lesen. Wenn dann kannst du meine Aussage falsch ist.Um zu verstehen, was Los ist, ich lese über das Java-Memory-Modell (JMM). Eine nützliche Einführung in die JMM können gefunden werden in Java Conurrency in der Praxis.
Ich denke, die Antwort auf die Frage: ja, in dem Beispiel machen die Mitglieder der Objekt volatile ist NICHT notwendig. Aber diese Umsetzung ist eher spröde, wie diese Garantie hängt von der genauen REIHENFOLGE, in der Dinge getan werden und auf die Thread-Sicherheit der Container. Ein builder pattern wäre eine viel bessere option.
Warum ist es garantiert:
Die add-Methode des thread-sichere container verwenden müssen einige Synchronisations-Konstrukte wie flüchtig Lesen /schreiben, zu sperren oder zu synchronisieren(). Dies garantiert zwei Dinge:
Die Objekte werden nicht verändert nach der Veröffentlichung.
Jedoch, wenn der container war nicht thread-sicher oder die Ordnung der Dinge verändert wurde und jemand sich nicht bewusst, das Muster oder die Objekte, die versehentlich verändert nach der Veröffentlichung, dann gibt es keine Garantien mehr. So, nach dem Builder-Muster können erzeugt werden, indem google AutoWert oder Freebuilder ist viel sicherer.
In diesem Artikel auf das Thema ist auch ziemlich gut:
http://tutorials.jenkov.com/java-concurrency/volatile.html