Questo è un problema che riscontro frequentemente nel lavorare con sistemi più complessi e che non ho mai trovato un buon modo per risolvere. Di solito comporta variazioni sul tema di un oggetto condiviso la cui costruzione e inizializzazione sono necessariamente due passaggi distinti. Ciò è generalmente dovuto a requisiti di architettura, simili alle applet, quindi le risposte che suggeriscono di consolidare la costruzione e l'inizializzazione non sono utili. I sistemi devono avere come target Java 4 al massimo, quindi le risposte che suggeriscono che il supporto disponibile solo in JVM successive non sono utili.Come posso creare un valore read-many write-once thread-safe in Java?
A titolo di esempio, diciamo che ho una classe che è strutturato per adattarsi in un framework applicativo in questo modo:
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
}
}
non posso fare someObject finale in quanto non può essere impostata fino all'avvio() è invocato. Ma mi piacerebbe davvero che riflettesse la sua semantica di sola scrittura e potessi accedere direttamente da più thread, preferibilmente evitando la sincronizzazione.
L'idea è quello di esprimere e far rispettare un certo grado di finalness, Suppongo che avrei potuto creare un contenitore generico, in questo modo (UPDATE - corretto sematics threading di questa classe):
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;
}
}
e poi in MyClass
, sopra, fare:
private final WormRef<SomeObject> someObject;
MyClass() {
someObject=new WormRef<SomeObject>();
}
public void startup() {
someObject.init(new SomeObject(...));
}
public void sometimeLater() {
someObject.get().doSomething();
}
che solleva alcune domande per me:
- C'è un modo migliore o un oggetto Java esistente (dovrebbe essere disponibile in Java 4)?
In secondo luogo, in termini di sicurezza filo:
- È questo thread-safe condizione che nessun altro thread accede
someObject.get()
fino a dopo la suaset()
è stato chiamato. Gli altri thread invocheranno solo metodi su MyClass tra startup() e shutdown() - il framework lo garantisce. - Dato il contenitore
WormReference
completamente non sincronizzato, è sempre possibile in JMM visualizzare un valore diobject
che non è nullo nè un riferimento a SomeObject? In altre parole, il JMM garantisce sempre che nessun thread possa osservare la memoria di un oggetto come qualsiasi valore presente nell'heap quando l'oggetto è stato allocato. Credo che la risposta sia "Sì" perché l'allocazione azzera in modo esplicito la memoria allocata, ma il caching della CPU può far sì che qualcos'altro venga osservato in una determinata posizione di memoria? - È sufficiente rendere volatile WormRef.reference per garantire una semantica multithread corretta?
Nota la spinta principale di questo problema è come esprimere e far rispettare la finalness di someObject
senza essere in grado di effettivamente segnare final
; secondario è ciò che è necessario per la sicurezza del filo. Cioè, non rimanere troppo bloccato sull'aspetto di sicurezza del thread di questo.
Suppongo che dire "non avvia gli altri thread finché non è inizializzata" non sta per essere accettabile? – MSN
@MSN: In realtà, nella maggior parte dei casi è un dato di fatto che gli altri thread non saranno nemmeno avviati fino a dopo l'inizializzazione. –
A tutti, ho cancellato la mia risposta perché l'OP stava insultando che ho modificato la sua domanda per imporre la mia risposta. Questo non ha senso. – BalusC