从给定的代码开始,“双重检查锁定”在某些环境中可能会损坏,当使用赛门铁克JIT在系统上运行时,它不起作用。特别是,赛门铁克 JIT 可编译
singletons[i].reference = new Singleton();
到下面(请注意,赛门铁克 JIT 使用基于句柄的对象分配系统)。
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
如您所见,对单例[i].引用的赋值是在调用单例构造函数之前执行的。这在现有的Java内存模型下是完全合法的,在C和C++中也是合法的(因为它们都没有内存模型)。
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
除此之外
- 如果类是
Serializable
- 如果它的“可克隆”,它可能会中断
- 你可以打破(我相信)
Reflection
- 它可以打破ff多个类装入器被装入类
*如何解决规则破坏者?
- 进行预先初始化要安全得多
- 为了防止反序列化以创建新对象,您可以在类中重写方法并引发异常
readResolve()
- 为了防止克隆,您可以过度引发并引发异常
clone()
CloneNotSupported
- 为了转义反射瞬时,我们可以在构造函数中添加check并抛出异常。
例
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();
}
}
毕竟,我建议使用Enum作为Singleton最安全的方式(因为java5最好的方法是使用枚举)
public static enum SingletonFactory {
INSTANCE;
public static SingletonFactory getInstance() {
return INSTANCE;
}
}