公共类单个{私有状态单个=空; publicstaticsingletongetinstance () if ) null==instance线程2检测到实例不为空。 已同步) singleton.class (if ) ) ) ) ) )。 //线程在被提示重新排序时,先执行赋值,但构造函数尚未运行() (即初始化尚未完成) } }返回实例; //之后执行线程2时,对象似乎已达到“发生尚未初始化的错误”}请求,但第一次创建对象以外的访问将在第一个if中返回,因此不去同步块。 已经完美了吗?
上面代码段的注释:如果线程执行到instance=new Singleton )语句,在此它看起来像一个词,但实际上,编译后在JVM中执行的对应关系会改变代码。 该语句被编译成八个汇编指令,大致有三件事要做。
1 )为实例分配内存
2 )初始化实例的构造函数
3 )将实例对象指向分配的内存区域(注意到此步骤时,实例不是null ) )。
如果指令按照顺序执行倒也无妨,但JVM为了优化指令,提高程序运行效率,允许指令重排序这样,程序实际运行时,上述指令的执行顺序可能如下。
a )为实例分配内存
b )将实例对象指向分配的内存区域;
c )初始化实例的构造函数
此时,如果线程执行完b ),则在执行c )之前切换到线程2,此时判断为实例不为空,此时线程2直接来到return instance语句,通过实例使用
对象尚未初始化
根据以上分析,具体来说就是synchronized虽然保证了线程的原子性(即synchronized块中的语句要么全部执行,要么一条也不执行),但单条语句编译后形成的指令并不是一个原子操作(即可能该条语句的部分指令未得到执行,就被切换到另一个线程了)
公共类singleton { privatevolatilestaticsingletoninstance=null; publicstaticsingletongetinstance () if ) null==instance ) synchronized ) Singleton.class ) if ) null==instance }