有关singleton模式中java double check失效问题

goodbaby728 2012-09-17 10:01:30
虽然从Java5之后可以通过将字段声明为volatile来避免了java中double check失效问题,但还是有点不大明白,主要由以下两点:

public class lazySingleton {
private static volatile lazySingleton m_instance=null;
private lazySingleton() {
// TODO Auto-generated constructor stub
System.out.println("构造函数");
}
public static lazySingleton getInstance(){
if(m_instance==null){
synchronized (lazySingleton.class) {
if(m_instance==null){
m_instance=new lazySingleton(); //疑惑一
}
}
}
return m_instance;
}
public void print(){
System.out.println("print");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
lazySingleton.getInstance().print();
}
}

1.能简要描述下到底是什么原因导致double check失效的吗?虽然很多地方看到是由于Java 编译器中LazySingleton类的初始化与instance变量的赋值顺序不可预料。以上代码“疑惑一”处,如果构造函数没有执行完毕,m_instance不是仍然为null吗?再说,如果构造函数没有执行完毕,会解锁吗?
2.外部加锁代码,每次都synchronized

synchronized public static lazySingleton getInstance(){
if(m_instance==null){
m_instance=new lazySingleton();
}
return m_instance;
}

为什么此处就不会出现上述的问题呢,个人感觉同样会出现一个线程的构造函数没有执行完毕,但已经对m_instance赋值,导致别的线程引用未知指向的m_instance吗?
...全文
567 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
goodbaby728 2012-09-20
  • 打赏
  • 举报
回复
感谢大家,懂了~之前是自己没搞清楚,第二种情况只要使用m_instance值都会进入synchronized排队,但第一种情况在获知m_instance不空后就不会进入synchronized,所以就会出现m_instance指向地址内容未初始化完毕的情况,也不是我之前想的那样m_instance为null~在此附上别处找到的编译器优化后的汇编代码吧,很清楚是先赋值,再初始化的~

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
goodbaby728 2012-09-20
  • 打赏
  • 举报
回复
感谢大家,懂了~之前是自己没搞清楚,第二种情况只要使用m_instance值都会进入synchronized排队,但第一种情况在获知m_instance不空后就不会进入synchronized,所以就会出现m_instance指向地址内容未初始化完毕的情况,也不是我之前想的那样m_instance为null~在此附上别处找到的编译器优化后的汇编代码吧,很清楚是先赋值,再初始化的~
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
MiceRice 2012-09-18
  • 打赏
  • 举报
回复
ticmy来了,这哥们是专家,楼主揪住他K吧,哈哈哈

话说我在JDK1.4上,完全测不出双检锁效果。

MiceRice 2012-09-18
  • 打赏
  • 举报
回复
不是你说的那个意思啊。

没加volatile的问题最多也只是B线程看不到A线程的赋值而已,那么无非是if条件成立,进入synchronized进行等待。

恰恰是加了volatile,才让B线程能更好的立即看到A线程的赋值;从而B线程可以调用一个无效对象。
龙四 2012-09-18
  • 打赏
  • 举报
回复
goodbaby728 2012-09-18
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 的回复:]

第一,你没有理解Singleton模式,要不然你不会问“如果构造函数没有执行完毕,m_instance不是仍然为null吗?”
第二,你也没有理解synchronized 放在方法前面的含义,不然你就会知道第二种方法是无论如何也不会出现问题的。
第三,我觉得第一段代码是没有问题的。


引用楼主 的回复:
虽然从Java5之后可以通过将字段声明为volatile来避免了java中d……
[/Quote]我一开始都说了,java5引入volatile之后是解决了问题,问题是不加volatile~你的批评我接受,当然我发帖更多是获得答案、解决问题,貌似你的回答很难让人满意啊~
龙四 2012-09-18
  • 打赏
  • 举报
回复
诚惶诚恐


volatile的作用:http://www.ticmy.com/?p=118

如果楼主只是从源码角度去看多线程代码,很多并发问题是看不出来的,如果以前没有接触过,可先查阅 java内存模型 (Java Memory Model) 相关资料


[Quote=引用 6 楼 的回复:]

ticmy来了,这哥们是专家,楼主揪住他K吧,哈哈哈

话说我在JDK1.4上,完全测不出双检锁效果。
[/Quote]
MiceRice 2012-09-17
  • 打赏
  • 举报
回复
双检锁失效的主要论点是:
m_instance=new lazySingleton();
这句话会被优化时的指令重排,以至于先赋值,再执行对象初始化:
m_instance = 对象内存位置;
对象.构造函数();
以至于某线程可能得到一个未初始化完毕的对象,所以关键问题已经不是在synchronized身上,而是 if

    public static lazySingleton getInstance(){
if(m_instance==null){ // B线程检测到m_instance不为空
synchronized (lazySingleton.class) {
if(m_instance==null){
m_instance=new lazySingleton(); // A线程被指令重排了,刚好先赋值了;但还没执行完构造函数。
}
}
}
return m_instance; // 后面B线程执行时将引发:对象尚未初始化错误。
}

不过至今没有很强力的证据,尤其是又有说法强调Java1.5及后续版本早已对该问题优化掉了,也即不会允许还没执行完构造函数就让new语句执行赋值的情况。
LogicTeamLeader 2012-09-17
  • 打赏
  • 举报
回复
第一,你没有理解Singleton模式,要不然你不会问“如果构造函数没有执行完毕,m_instance不是仍然为null吗?”
第二,你也没有理解synchronized 放在方法前面的含义,不然你就会知道第二种方法是无论如何也不会出现问题的。
第三,我觉得第一段代码是没有问题的。


[Quote=引用楼主 的回复:]
虽然从Java5之后可以通过将字段声明为volatile来避免了java中double check失效问题,但还是有点不大明白,主要由以下两点:

Java code


public class lazySingleton {
private static volatile lazySingleton m_instance=null;
private lazySin……
[/Quote]

51,394

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧