java单例双重检查锁为什么需要加volatile关键字

原文地址:http://blog.csdn.net/jason0539/article/details/23297037/ 回复中的13楼

Re: 炸斯特 2015-09-04 10:49发表 [回复] [引用] [举报]

回复qq_30486849:我的理解,volatile是要保证可见性,即instance实例化后马上对其他线程可见,而synchronized能同时保证原子性和可见性,同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中,保证当其他线程再进入的时候,在主存中读取到的就是最新的变量内容了。所以只要synchronized就够了。

Re: AC是男孩 3分钟前发表 [回复]

回复炸斯特:今天面试被问到这个问题,直接懵逼,在这里找到答案,谢谢。

Re: 炸斯特 2015-09-04 15:30发表 [回复] [引用] [举报]

回复炸斯特:刚刚想了想一些博客里面确实有提到一定要加volatile关键字,但是我的理解跟这篇博客类似http://www.jianshu.com/p/977d27852826,所以感觉不用加volatile,这一部分回去看一下纸面资料,博客内容终归复制粘贴的多,这种细节没有考究,如果有别的观点欢迎讨论

Re: 炸斯特 2015-09-04 18:02发表 [回复] [引用] [举报]

回复炸斯特:已经修改,的确应该加上volatile关键字。不加的情况下,假设两个线程,线程A正在执行instance = new Instance()的操作,而线程B开始执行if(instance==null)的判断,当不存在volatile的时候,因为 new Instance()是一个非原子操作,可能发生无序写入,构造函数可能在整个对象构造完成前执行完毕,线程B可能会看到一个不完整的instance对象,因为java的某些实现会在内存中开辟一片存储对象的区域后直接返回内存的引用,所以线程B判断不为null,而这时候实际上,instance的构造函数还没有执行,从而线程b得到不完整的对象。在 Instance 的构造函数执行之前,会在内存中开辟一片存储对象的区域后直接返回内存的引用,赋值给变量 instance,instance也就可能成为非 null 的,即赋值语句在对象实例化之前调用,此时别的线程得到的是一个还会初始化的对象,这样会导致系统崩溃线程B可能会看到一个不完整的instance对象,因为java的某些实现,所以线程B判断不为null。从而得到不完整的对象。

Re: 炸斯特 2015-12-26 15:47发表 [回复] [引用] [举报]

回复炸斯特:今天再看这段话有些歧义。假设没有关键字volatile的情况下,两个线程A、B,都是第一次调用该单例方法,线程A先执行instance = new Instance(),该构造方法是一个非原子操作,编译后生成多条字节码指令,由于JAVA的指令重排序,可能会先执行instance的赋值操作,该操作实际只是在内存中开辟一片存储对象的区域后直接返回内存的引用,之后instance便不为空了,但是实际的初始化操作却还没有执行,如果就在此时线程B进入,就会看到一个不为空的但是不完整(没有完成初始化)的Instance对象,所以需要加入volatile关键字,禁止指令重排序优化,从而安全的实现单例。

Re: 炫铭童话 昨天 15:52发表 [回复]

回复炸斯特:正解,创建对象可以分解为如下的3行伪代码memory=allocate(); //1:分配对象的内存空间ctorInstance(memory); //2:初始化对象instance=memory; //3:设置instance指向刚分配的内存地址上面3行代码中的2和3之间,可能会被重排序导致先3后2,

Re: June 2016-07-07 18:12发表 [回复] [引用] [举报]

回复炸斯特:赞,这个解释比上个解释清晰,而且正确。

    原文作者:java锁
    原文地址: https://blog.csdn.net/xiakepan/article/details/52444565
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞