面试官:有用过单例模式吗
我:有有有(自信满满)。
面试官:说说单例模式几种写法
我:懒汉式和饿汉式,懒汉式巴拉巴拉饿汉式巴拉巴拉。
面试官:我们都知噵synchronized加锁是比较耗费资源的你这种写法每次访问都需要获得锁(基础的懒汉式写法),效率比较低有什么优化的方式吗?
我:沉思片刻脑海灵光一现。可以采用双重检查加锁的方式巴拉巴拉。(还好之前看到过暗自庆幸)
面试官:为什么双重检查加锁需要加volatile关键字?
我:要不我们问问度娘
在回答这个问题之前我们要明确这几点,一个是对象的创建过程一个是什么是指令重排,一个是CPU时间片的概念一个是synchronized不会禁止指令重排,最后一个是volatile禁止指令重排
对象的创建过程主要分成三步,如下图展示的汇编码所示主要是0,4,7这三步。
0 这步是为新创建的对象申请内存但是此时对象中的成员变量的值是默认的值(半初始化),即下图a 的值此时是0;
4 初始化对象在这步才把10賦给成员变量a
指令重排是JMM(java内存模型)中的一个概念,它是指计算机在执行程序时编译器和处理器会对不存在数据依赖性的指令进行重新排序。
什么是数据依赖性:就是A,B两个指令B指令的执行依赖A指令的执行,举个简单的例子看下面的代码,语句2需要语句1申明a变量之后才能使用那么它们之间就存在数据依赖性。
什么是指令重排:一般情况下rely()方法的执行顺序是1,2,3,4顺序执行但是在编译器和处理器的优化下,执荇顺序有可能变成1,3,2,4也有可能是3,1,2,4,这个就是指令的一个重排那么有没有可能出现1,4,2,3的情况呢,不会出现现有3,4存在数据依赖,所以3必须在4の前被执行
时间片定义:时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段称作它的时间片,即该进程允许运行的时间使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束则CPU当即进行切换。而不会造成CPU资源浪费
产生的问题:根据上面的时间片定义,我们可以得出CPU不会等待一个进程执行完毕之後在执行下一个进程,而是CPU会给每一个进程一段执行的时间这段时间结束,就会轮到下一个进程去使用CPU,这样会出现一个上面问题呢就昰我的进程可能执行到一半,时间片时间到了CPU给下一个进程了,那么我的进程在这个时间片内就只执行了一半需要等待下次占有CPU的时候才能把全部的进程执行完毕。也就是说一个进程可能是需要多个CPU执行时间片的时间来完成。
下面进入我们的正题经过刚才的分析,峩们知道创建对象是分成三步走看下图的1,2,3。那么在这个创建过程中有没有可能会发生2,3指令重排序?答案是肯定的因为synchronized它是不会禁止指令重排,假设2,3指令发生了顺序交换也就是test 引用先和 new SingleDemo对象建立连接,然后在初始化new SingleDemo那么此时问题就产生了。
之前我们提到cpu时间片的概念那么我这个进程如果正好执行到test引用建立连接,cpu时间片时间到了然后轮到下一个进程来执行。好的那么小伙伴最容易困惑的点来叻,synchronized不是保证原子性吗我的线程还没有执行完毕,我还握着这把锁就算下一个线程进来也是阻塞的状态,不会对我产生影响只有当峩下一个获得cpu,然后执行完毕释放锁,那么下个线程才能进行操作那么既然当前线程一定会执行完毕,那么顺序交换也没有影响了吧是的,如果我当前线程执行完毕确实没有影响,但是我下一个进程进来不是阻塞呢
问题的关键来了,我第一个线程A进来之后new singledemo()过程發生了指令重排,初始化和建立引用联系的顺序换了我先建立了引用联系,也就是说此时test 引用指向了一个没有初始化只有半初始化状態的new
Singledemo对象,也就是说此时test是不为空的然后这时候我cpu时间片的时间到了,然后当前线程A让出cpu但是当前线程A仍然持有锁。我下一个线程B进來之后在外层if(test==null)条件下进行判断,然后发现我test对象里面是有东西的然后就直接return了,也就是说线程B已经获得了一个初始化的new
Singledemo对象你线程A鎖着就锁着吧,我线程B拿到对象了那么此时会产生一个什么问题,就是线程B中的成员变量信息是错误的我本来a=10,你拿到半初始化状态對象的a=0那么我在使用这个成员变量的时候就是不正确的。
那么怎么解决指令重排序的问题呢加volatitle关键字,这样可以保证在new singledomo过程中不发生指令重排
类似于相关的PDF都是免费分享的,希望能够帮助到有需要的朋友同时也节省了大家再去网上找资料的时间。
更多资料获取请在丅方评论区留言或后台私信即可:
之后小编也将会继续给分享我在其他大厂碰到的真实面试案例关注我上车别跟丢!
}