五,证明上边的假想是可能发生的,字节码是用来分析问题的最好的工具,可以利用它来分析下边一段程序(为了分析方便,所以渐少了内容):
字节码的使用方法见这里,利用字节码分析问题:
{
private static Singleton instance;
private boolean inUse;
private int val;
private Singleton()
{
inUse = true;
val = 5;
}
public static Singleton getInstance()
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
得到的字节码:
054D20B0 mov eax,[049388C8] ;load instance ref
054D20B5 test eax,eax ;test for null
054D20B7 jne 054D20D7
054D20B9 mov eax,14C0988h
054D20BE call 503EF8F0 ;allocate memory
054D20C3 mov [049388C8],eax ;store pointer in
;instance ref. instance
;non-null and ctor
;has not run
054D20C8 mov ecx,dword ptr [eax]
054D20CA mov dword ptr [ecx],1 ;inline ctor - inUse=true;
054D20D0 mov dword ptr [ecx+4],5 ;inline ctor - val=5;
054D20D7 mov ebx,dword ptr ds:[49388C8h]
054D20DD jmp 054D20B0
上边的字节码证明,猜想是有可能实现的六:好了,上边证明Double-checked locking可能出现取出错误数据的情况,那么我们还是可以解决的:
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
inst = new Singleton(); //4
}
instance = inst; //5
}
}
}
return instance;
}
利用Double-checked locking 两次同步,中间变量,解决上边的问题。
下边这段话我只能简单的理解,翻译过来不好,所以保留原文,list 7是上边的代码,list 8是下边的。
The code in Listing 7 doesn't work because of the current definition of the memory model.
The Java Language Specification (JLS) demands that code within a synchronized block not be moved out of a synchronized block. However, it does not say that code not in a synchronized block cannot be moved into a synchronized block.
A JIT compiler would see an optimization opportunity here.
This optimization would remove the code at //4 and the code at //5, combine it and generate the code shown in Listing 8:
list 8
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
//inst = new Singleton(); //4
instance = new Singleton();
}
//instance = inst; //5
}
}
}
return instance;
}