二狗:二胖你昨天请假了是否是又去面试了啊?
二胖:别说了我就出去试试水,看看如今工做好很差找,顺带出去找找打击,而后才能好好静下心来好好学习。
二狗: 那被打击的怎么样啊?知道本身是什么样的水平了吧,坏笑。
二胖:基础太差,一面就让回去等通知了,我要好好学习了,不跟你瞎扯了。
二狗: 都问了你什么问题啊,把你打击成这样?一块儿复盘下让我也好好准备下啊。
二胖:好吧,你既然这么好奇,那我就大概说下吧,你搬上小板凳仔细挺好了哦。我要开始个人表演了。
下面二胖第一面开始了。
面试官:二胖是吧,先作个自我介绍吧。
二胖:好的,我叫二胖,我来自长沙,今年25岁,从事java开发快3年了,如今在XX公司XX事业部担任高级java开发工程师,主要负责XX系统。。。。。
面试官:好的,我看你简历上写着熟练掌握并发编程你能跟我说说并发编程里面你都知道哪些关键字。
二胖: 这不就是要考我 synchronized
和volatile
这个我擅长啊,我特地背过的,synchronized
是java
提供的一个关键字它主要能保证原子性、有序性它的底层主要是经过Monitor
来实现的。volatile
也是java的一个关键字它的主要做用是能够保证可见性。。。。此处省略1000字。
面试官:八股文背的不错,说了这么多,咱们来动手试试吧,写一个双重校验锁(dcl)的单例我看看。
二胖: 从屁股口袋里拿出了笔三下五除二就把它默写出来了。
面试官:你有说道volatile
关键字和synchronized
关键字。synchronized
能够保证原子性、有序性和可见性。而volatile
却只能保证有序性和可见性。那么,咱们再来看一下双重校验锁实现的单例,已经使用了synchronized
,为何还须要volatile
?这个volatile
是否能够去掉?
二胖: 让我想一想,貌似好像确实能够去掉。
面试官: 咱们今天的面试就到这里吧,后续有消息人事会联系你,感谢你今天来面试。java
二胖很郁闷回去谷歌了下这个问题,stackoverflow上也有这个问题,看样子不仅我一我的不知道这个问题吗?看样子面试挂的不冤
以上故事纯属虚构,若有雷同请以本文为主。面试
咱们先来看看没有加volatile 修饰的单例:编程
1 public class Singleton { 2 private static Singleton singleton; 3 private Singleton (){} 4 public static Singleton getSingleton() { 5 if (singleton == null) { 6 synchronized (Singleton.class) { 7 if (singleton == null) { 8 singleton = new Singleton(); 9 } 10 } 11 } 12 return singleton; 13 } 14 }
上述代码看下来是否是感受没啥问题。
首先咱们先来看下这一行代码到底干了哪些事情并发
singleton = new Singleton()
上述过程咱们能够简化成3个步骤:post
这个步骤有两种执行顺序能够按照 ①②③或者①③②来执行。当咱们按照①③②的顺序来执行的时候
咱们假设有两个线程ThreadA
和ThreadB
同时来请求Singleton.getSingleton
方法:学习
第一步:ThreadA
进入到第8行,执行 singleton = new Singleton()
进行对象的初始化(按照对象初始化的过程 ①②③)执行完。
第二步: ThreadB
进入第5
行判断singleton
不为空(第一步已经初始化好了),直接返回singleton
第三步:拿到这个对象作其余的操做。
这样看下来是否是没有啥问题。优化
第一步: ThreadA
进入到第8行,执行 singleton = new Singleton()
执行完.①JVM
为对象分配一块内存M
。③将内存的地址复制给singleton
变量。
第二步: 此时ThreadB
直接进入第5行,发现singleton
已经不为空了而后直接就跳转到12
行拿到这个singleton
返回去执行操做去了。此时ThreadB
拿到的singleton
对象是个半成品对象,由于尚未为这个对象进行初始化(②还没执行)。
第三步: 因此ThreadB
拿到的对象去执行方法可能会有异常产生。至于为何会这样列?《Java 并发编程实战》有提到spa
有 synchronized 无 volatile 的 DCL(双重检查锁) 会出现的状况:线程可能看到引用的当前值,但对象的状态值确少失效的,这意味着线程能够看到对象处于无效或错误的状态。
说白了也就是ThreadB是能够拿到一个引用已经有了可是内存资源尚未分配的对象。
若是要解决建立对象按照①②③的顺序,其实也就是为了解决指令重排只要第2行加个volatile
修饰就好。
说好的synchronized 不是能够保证有序性的吗?volatile的有序性?synchronized 不能不够保证指令重排吗?
怎么来定义顺序呢?《深刻理解Java虚拟机第三版》有提到线程
Java程序中自然的有序性能够总结为一句话:若是在本线程内观察,全部操做都是自然有序的。若是在一个线程中观察另外一个线程,全部操做都是无序的。前半句是指“线程内似表现为串行的语义”,后半句是指“指令重排”现象和“工做内存与主内存同步延迟”现象。
站在巨人的肩膀上摘苹果:
https://stackoverflow.com/que...
https://juejin.cn/post/684490...code