Java中的双重检查锁(double checked locking)分析

Java中的双重检查锁(double checked locking)分析

分析通过双重检查锁来获得一个单例时,我主要从3个方面来考虑:

.
① synchronized: 通过在getInstance方法上加上同步锁,进行线程控制


② 双重if判断: 为了避免了在首层判断就加上Synchorzied同步锁,导致锁的粒度过大,导致效率的低下 ,所以采用双重if判断,在第二层判断才引入对性能开销较大的synchorzied锁,双重if判断本质上是对效率的优化


③ volatile : 另外,为了必免jvm在指令优化时,对创建对象(new)过程出现的指令重排序现象,需要组引用对象用volatile修饰

下面我们正式开始分析

相信,我们在解决并发获得单例时,第一想到的就在getInstance方法上加上同步锁。
代码如下:

public class Singleton {
private static Singleton uniqueSingleton;

private Singleton() {
}

public synchronized Singleton getInstance() {
if (null == uniqueSingleton) {
uniqueSingleton = new Singleton();
}
return uniqueSingleton;
} 
}    

但往往,每个线程在getInstance方法时,都要拿到同步锁,会使得系统开销增大,有点没必要。

优化方案
当已经有一个实例后,通过双重判断,可以使得以后的线程在调用getInstance方法不需要获得同步锁,效率优化。

public class Singleton {
private static Singleton uniqueSingleton;

private Singleton() {
}

public Singleton getInstance() {
if (null == uniqueSingleton) {
synchronized (Singleton.class) {
if (null == uniqueSingleton) {
uniqueSingleton = new Singleton();   // error
}
}
}
return uniqueSingleton;
}
}   

但还有一个问题,在有些情况下,通过这种方式拿到的Singleton对象,可能是错误的 。

为什么呢?

回顾我们new对象的3个步骤

① 分配内存空间

② 初始化对象

③ 将对象指向刚分配的内存空间

但jvm在指令优化时,会出现步骤②和③对调的情况,比如线程1在经过俩层为null判断后,进入new的动作,在还没有初始化对象时,就返加了地址值,线程2在第一个为null判断时,因为对象已经不为空,那么就直接返回了对象。

这时,就会出现问题。
(虽然我没遇到过此问题,也不清楚这种重排序在实际应用中会导致什么,但大佬都很怕这个问题)

那么,怎么解决呢? 在引用对象前加上volatile关键字,可以解决jvm重排序问题

代码如下

public class Singleton {
private volatile static Singleton uniqueSingleton;

private Singleton() {
}

public Singleton getInstance() {
if (null == uniqueSingleton) {
synchronized (Singleton.class) {
if (null == uniqueSingleton) {
uniqueSingleton = new Singleton();
}
}
}
return uniqueSingleton;
}
}   

使用volatile关键词主要可以保证代码的执行顺序不受jvm重排序影响,所以就上面的问题,对象肯定会在初始化后才会返回内存的地址值。

有关volatite的更多介绍,需要借助jvm内存模型,可以参考本人转载文章volatile关键字解析

End!

18-11-11 修正 于松江图书馆


在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值