Das Double-checked locking soll ja angeblich den Zugriff auf die Instanziierung mittels einer zweiten Bedingung schützen. Das soll sicherstellen, dass die Instanziierung nur durchgeführt wird, wenn wirklich noch keine Instanz angelegt wurde.
Das könnte doch funktionieren... wäre da nicht das Speichermodell der Java-Plattform! Die zweite Bedingung if (instance == null) kann nämlich zu true evaluieren, ohne dass new DoubleCheckedLockingSingleton() aufgerufen und das instanziierte Objekt dem Klassenattribut instance zugewiesen wurde!
Um das exemplarisch zu verdeutlichen, zeige ich den Pseudo-Bytecode für den Befehl instance = new DoubleCheckedLockingSingleton() an:
1.// Speicher alloziieren
ptrMemory = allocateMemory()
2.// Den Speicher dem Klassenattribut zuweisen, ab hier gilt: instance != null
assignMemory(instance, ptrMemory)
3.// Den Konstruktor aufrufen, das Objekt ist dann ab hier korrekt instanziiert
callDoubleCheckedLockingSingletonConstructor(instance)
Zwischen der zweiten und dritten Zeile könnte die Ausführung des Threads durch die Java Virtual Machine unterbrochen, und die Ausführung eines zweiten Threads vorgezogen werden. Die zweite Bedingung if (instance == null) würde damit zu true evaluieren, ohne dass bisher der Konstruktor (Zeile 3) aufgerufen worden wäre.
Das double-checked locking ist damit nicht sicher und die mögliche Lösung dafür auch nicht oder sehe ich das falsch?
-------------------------------------------------------------------------------------------------------------------------
//is bad
-------------------------------------------------------------------------------------------------------------------------
//should be good
Das könnte doch funktionieren... wäre da nicht das Speichermodell der Java-Plattform! Die zweite Bedingung if (instance == null) kann nämlich zu true evaluieren, ohne dass new DoubleCheckedLockingSingleton() aufgerufen und das instanziierte Objekt dem Klassenattribut instance zugewiesen wurde!
Um das exemplarisch zu verdeutlichen, zeige ich den Pseudo-Bytecode für den Befehl instance = new DoubleCheckedLockingSingleton() an:
1.// Speicher alloziieren
ptrMemory = allocateMemory()
2.// Den Speicher dem Klassenattribut zuweisen, ab hier gilt: instance != null
assignMemory(instance, ptrMemory)
3.// Den Konstruktor aufrufen, das Objekt ist dann ab hier korrekt instanziiert
callDoubleCheckedLockingSingletonConstructor(instance)
Zwischen der zweiten und dritten Zeile könnte die Ausführung des Threads durch die Java Virtual Machine unterbrochen, und die Ausführung eines zweiten Threads vorgezogen werden. Die zweite Bedingung if (instance == null) würde damit zu true evaluieren, ohne dass bisher der Konstruktor (Zeile 3) aufgerufen worden wäre.
Das double-checked locking ist damit nicht sicher und die mögliche Lösung dafür auch nicht oder sehe ich das falsch?
-------------------------------------------------------------------------------------------------------------------------
//is bad
Code:
public class v5_DoubleCheckedLockingSingleton {
private static v5_DoubleCheckedLockingSingleton instance = null;
private v5_DoubleCheckedLockingSingleton() {}
public static v5_DoubleCheckedLockingSingleton getInstance() {
if (instance == null) {
synchronized (v5_DoubleCheckedLockingSingleton.class) {
if (instance == null) {
instance = new v5_DoubleCheckedLockingSingleton();
}
}
}
return instance;
}
}
//should be good
Code:
public class ThreadLocalDCL {
private static ThreadLocal<Boolean> initHolder = new ThreadLocal<Boolean>();
private static ThreadLocalDCL instance = null;
public static ThreadLocalDCL getInstance() {
System.out.println(Thread.currentThread().getName() + " " + initHolder.get());
if (initHolder.get() == null){
synchronized (ThreadLocalDCL.class) {
if (instance == null) {
instance = new ThreadLocalDCL();
}
initHolder.set(Boolean.TRUE);
}
}
System.out.println(Thread.currentThread().getName() + " " + initHolder.get());
return instance;
}
}