天天看点

Java面试题解析:在DCL单例写法中为什么要做两次检查?一位工作5年的小伙伴在面试的时候被问到这样一道题:DCL的单例

作者:Tom弹架构

Java面试题解析:在DCL单例写法中为什么要做两次检查?

一位工作5年的小伙伴在面试的时候被问到这样一道题:DCL的单例写法中为什么要做两次检查?要回答好这个问题,需要知道DCL的单例写法以及它为什么要这样写?今天给大家详细分析一下。这道面试题的文字版我已经整理在20万字的文档里面了,有需要的小伙伴可以在评论区领取。

什么是nrl?DCL是一种单例模式写法的简称,它的全称是Double Check Lock,翻译过来叫做双重检查锁。从命名上来理解它就是两次检查加一把锁。两次检查又是检查什么呢?锁又是锁的什么呢?首先来看这样一段代码,这是一段比较标准的DCL的单例写法。

在代码中发现两次检查的判断条件都是null==instance,而两个检查条件是嵌套的。在第一次检查条件的代码块中加了一段synchronized代码块,而synchronized它就是锁。小伙伴们应该都知道加锁是为了去保证线程要全,而检查是为了去保证内存中只有一个实例。既然这两个条件是嵌套的,那是不是可以去掉一个条件呢?下面我来详细分析一下。

为什么需要两次检查?重点来看这一个代码片段。假设去掉第一次检查只保留第二次检查。如代码所示,现在有两个线程T1和T2同时去访问getinstance()方法。当T1进入到synchronized代码块的时候T2就会阻塞,直到T1执行完成释放CPU资源以后T2才能够获得锁。

T1执行检查条件的时候null==instance,它的条件为ture,也就是满足条件,因此它会去创建一个新的对象。而T2执行检查条件的时候不满足条件就会直接返回T1创建好的对象,这样就保证了单例。

可是问题来了,后续如果再有其它的线程T4T5T6出现并发的时候,也会同时调用getinstance()方法。这种情况依然会出现阻塞。

相当于是不管单例对象是否已经创建,每次调用都可能出现阻塞就会影响程序的执行效率。所以加上第一次检查的目的是保证只有第一次出现并发的情况下才会阻塞,从而去提高性能。

再假设去掉第二次检查只保留第一次检查,如代码所示,还是线程T1、T2,同时访问getlnstance()方法,T1和T2同时满足条件,两个线程就会按顺序来执行synchronized代码块中的逻辑。

假设T1先执行创建对象,T2获得锁的时候就依然会创建对象,而且还会覆盖T1创建的对象,这个时候就相当于是破坏了单例。因此第二次检查的目的是为了去保证单例,避免去重复创建单例对象。

通过前面的分析可以得出结论:DCL的单例写法中第一次检查是为了去保证,只有首次并发情况下才会阻塞,从而提高性能。第二次检查是为了去保证单例,避免重复创建对象。最后加锁当然是为了去保证线程全了。

在前面的分享中我还有一个细节没有讲到,就是在并发情况下new一个对象可能会出现指令重排的现象,这个时候就需要给声明的单例对象加上一个volatile关键字来保证可见性。如代码所示。至于沃尔沃尔格尔关键字为什么能够解决指令重排的问题,小伙伴们可以去我的主页在往期视频中专门有详细分析这个问题。本期视频就不重复讲解了。

以上就是我对DCL两次检查的理解。我是被编程耽误的文艺Tom,如果我的分享对你有帮助,请你动动手指一键三连分享给更多的人。关注我面试不再难。

Java面试题解析:在DCL单例写法中为什么要做两次检查?一位工作5年的小伙伴在面试的时候被问到这样一道题:DCL的单例
Java面试题解析:在DCL单例写法中为什么要做两次检查?一位工作5年的小伙伴在面试的时候被问到这样一道题:DCL的单例
Java面试题解析:在DCL单例写法中为什么要做两次检查?一位工作5年的小伙伴在面试的时候被问到这样一道题:DCL的单例
Java面试题解析:在DCL单例写法中为什么要做两次检查?一位工作5年的小伙伴在面试的时候被问到这样一道题:DCL的单例
Java面试题解析:在DCL单例写法中为什么要做两次检查?一位工作5年的小伙伴在面试的时候被问到这样一道题:DCL的单例
Java面试题解析:在DCL单例写法中为什么要做两次检查?一位工作5年的小伙伴在面试的时候被问到这样一道题:DCL的单例
Java面试题解析:在DCL单例写法中为什么要做两次检查?一位工作5年的小伙伴在面试的时候被问到这样一道题:DCL的单例

继续阅读