Starting with your given code, "Double-Checked Locking" can be broken at some environment,
When run on a system using the Symantec JIT, it doesn't work. In particular, the Symantec JIT compiles
singletons[i].reference = new Singleton();
to the following (note that the Symantec JIT using a handle-based object allocation system).
0206106A mov eax,0F97E78h
0206106F call 01F6B210 ; allocate space for
; Singleton, return result in eax
02061074 mov dword ptr [ebp],eax ; EBP is &singletons[i].reference
; store the unconstructed object here.
02061077 mov ecx,dword ptr [eax] ; dereference the handle to
; get the raw pointer
02061079 mov dword ptr [ecx],100h ; Next 4 lines are
0206107F mov dword ptr [ecx+4],200h ; Singleton's inlined constructor
02061086 mov dword ptr [ecx+8],400h
0206108D mov dword ptr [ecx+0Ch],0F84030h
As you can see, the assignment to singletons[i].reference is performed before the constructor for Singleton is called. This is completely legal under the existing Java memory model, and also legal in C and C++ (since neither of them have a memory model).
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Apart from that
- It can break if the class is
Serializable
- It can break if its 'Clonable`
- You can break by
Reflection
(I believe)
- it can break ff multiple classloaders are loaded the class
*How do you solve rule breakers?
- It is much safer to do eager initialization
- To prevent deserializing to create new object
you may override
readResolve()
method in your class and throw exception
- To prevent cloning,
you may overrride
clone()
and throw CloneNotSupported
exception
- To escape for reflective instantion, we can add check in the constructor and throw exception.
Example
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// Check if we already have an instance
if (INSTANCE != null) {
throw new IllegalStateException("Singleton" +
" instance already created.");
}
}
public static final Singleton getInstance() {
return INSTANCE;
}
private Object readResolve() throws ObjectStreamException {
return INSTANCE;
}
private Object writeReplace() throws ObjectStreamException {
return INSTANCE;
}
public Object clone() throws CloneNotSupportedException {
// return INSTANCE
throw new CloneNotSupportedException();
}
}
After all I would suggest to use Enum as the safest way for Singleton (Since java5 the best way to do it is to use an enum)
public static enum SingletonFactory {
INSTANCE;
public static SingletonFactory getInstance() {
return INSTANCE;
}
}
private static Singleton singleInstance= new Singleton()
? – LaurettalaurettesingleInstance
before the constructor runs, this means that another thread may access the uninitialized singleton which can lead to errors – Embolism