Dies ist ein Problem, auf das ich häufig stoße, wenn ich mit komplexeren Systemen arbeite und das habe ich nie gut gelöst. Es beinhaltet normalerweise Variationen über das Thema eines gemeinsamen Objekts, dessen Konstruktion und Initialisierung notwendigerweise zwei verschiedene Schritte sind. Dies ist im Allgemeinen aufgrund von Architekturanforderungen ähnlich wie Applets, so dass Antworten, die vorschlagen, dass ich Konstruktion und Initialisierung konsolidiere, nicht nützlich sind. Die Systeme müssen spätestens auf Java 4 ausgerichtet sein, so dass Antworten, die eine Unterstützung vorschlagen, die nur in späteren JVMs verfügbar ist, ebenfalls nicht nützlich sind.Wie erstelle ich in Java einen threadsicheren Einmallesewert?
Als Beispiel, sagen wir, ich eine Klasse, die in einen Anwendungsrahmen passen, wie so strukturiert ist:
public class MyClass
{
private /*ideally-final*/ SomeObject someObject;
MyClass() {
someObject=null;
}
public void startup() {
someObject=new SomeObject(...arguments from environment which are not available until startup is called...);
}
public void shutdown() {
someObject=null; // this is not necessary, I am just expressing the intended scope of someObject explicitly
}
}
Ich kann nicht endgültig machen someobject, da es nicht bis zur Inbetriebnahme festgelegt werden kann() wird aufgerufen. Aber ich möchte wirklich, dass es seine Semantik zum Einmalschreiben widerspiegelt und in der Lage ist, direkt von mehreren Threads darauf zuzugreifen, vorzugsweise um eine Synchronisation zu vermeiden.
Die Idee ist, zum Ausdruck bringen und ein gewisses Maß an finalness zu erzwingen, ich vermute, dass ich einen generischen Container erstellen könnte, wie so (UPDATE - korrigiert Threading sematics dieser Klasse):
public class WormRef<T>
{
private volatile T reference; // wrapped reference
public WormRef() {
reference=null;
}
public WormRef<T> init(T val) {
if(reference!=null) { throw new IllegalStateException("The WormRef container is already initialized"); }
reference=val;
return this;
}
public T get() {
if(reference==null) { throw new IllegalStateException("The WormRef container is not initialized"); }
return reference;
}
}
und dann in MyClass
, oben, tun:
private final WormRef<SomeObject> someObject;
MyClass() {
someObject=new WormRef<SomeObject>();
}
public void startup() {
someObject.init(new SomeObject(...));
}
public void sometimeLater() {
someObject.get().doSomething();
}
, die einige Fragen für mich aufwirft:
- Gibt es einen besseren Weg, oder existierendes Java-Objekt (müsste in Java 4 verfügbar sein)?
In zweiter Linie, die Sicherheit in Bezug auf Gewinde:
- Ist das Thread-sicher vorgesehen, die kein anderer Thread greift
someObject.get()
erst nach seinemset()
aufgerufen wurde. Die anderen Threads rufen nur Methoden auf MyClass zwischen startup() und shutdown() auf - das Framework garantiert dies. - Angesichts der völlig unsynchronisierten
WormReference
Container ist es immer möglich, unter JMM einen Wert vonobject
, die weder Null noch ein Verweis auf ein SomeObject? Mit anderen Worten, garantiert das JMM immer, dass kein Thread den Speicher eines Objekts so beobachten kann, dass es sich auf die Werte bezieht, die sich bei der Zuweisung des Objekts auf dem Heapspeicher befanden. Ich glaube, die Antwort lautet "Ja", weil die Zuweisung den zugewiesenen Speicher explizit auf Null setzt - aber kann CPU-Caching dazu führen, dass an einem bestimmten Speicherort etwas anderes beobachtet wird? - Ist es ausreichend, WormRef.reference volatile zu machen, um richtige Multithread-Semantik zu gewährleisten?
Hinweis der primäre Stoßrichtung dieser Frage ist, wie man die finalness von someObject
ohne in der Lage zu markieren es tatsächlich final
auszudrücken und durchzusetzen; Sekundär ist, was für die Fadensicherheit notwendig ist. Das heißt, nicht auf den Thread-Sicherheitsaspekt davon hängen.
I „nicht die anderen Threads starten, bis er initialisiert ist“ nehme sagen, wird nicht akzeptabel? – MSN
@MSN: Tatsächlich ist es in den meisten Fällen selbstverständlich, dass die anderen Threads erst nach der Initialisierung gestartet werden. –
An alle löschte ich meine Antwort, weil das OP beleidigend war, dass ich seine Frage redigierte, um meine Antwort aufzuerlegen. Das macht keinen Sinn. – BalusC