关于双重检测锁的一种无volatile实现

本文已同步到http://liumian.win/2016/12/17/dcl-without-volatile/

上一篇博客中提到双重检测锁的无volatile实现,如何实现呢?那么在这篇博客中来一探究竟吧~

无volatile的安全实现

先上代码

/**
 * Created by liumian on 2016/12/13.
 */
public class DCL {

    private static DCL instance;

    private DCL(){}

    private DCL getInstance(){
        if (instance == null){              //1
            synchronized (DCL.class){       //2
                if (instance == null){      //3
                   DCL temp = new DCL();    //4
                   temp.toString();         //5
                   instance = temp;         //6
                }
            }
        }
        return instance;                    //7
    }
}

再分析原因

无volatile修饰的DCL归根结底是对象的不安全发布:对象还没有构造好,就将其发布出去了。

仔细与不安全的(无volatile)的DCL相比较,我们在同步代码块里面这三行代码发生了改变:

DCL temp = new DCL();    
temp.toString();         
instance = temp;         

对应三个步骤:
1. 创建临时变量
2. 调用临时变量的方法
3. 将临时变量的引用赋值给单例变量

为什么要引入临时变量呢?
为什么要调用临时变量的方法呢?
大家在心里肯定会有这些疑问,别急,我来一个一个的回答。

  1. 为什么要引入临时变量?
    引入临时变量的目的是将对象的初始化(new、invokespecial)与赋值(astore)强行分开。但是仅仅通过一个临时变量中转是不够的,请看下面这个问题的分析。

  2. 为什么要调用临时变量的方法?
    如果没有调用临时变量的方法这一行代码:

    DCL temp = new DCL();   
    instance = temp;         

    并不能解决解决根本问题:对象的不安全发布。因为JVM依然有可能对这三条指令:new、involvespecial以及两条astore指令(分别对应的是赋值给temp,temp赋值给instance,因为线程内表现为串行的语义的存在,这两条赋值指令的顺序不能改变),所以对于JVM来说那两行代码和这一行代码并没有特殊的地方:

    instance = new DCL();      

现在问题的关键便落在了temp.toString();
上一个问题的回答中我们提到了解决问题的思路:将对象的初始化(new、invokespecial)与赋值(astore)强行分开。其实这行代码起到的作用相当于一个内存屏障,将两个操作(初始化和赋值)强行分开。
因为线程内表现为串行的语义的存在,以及Happens-Before的第一条规则:程序次序规则(什么是线程内表现为串行的语义和程序次序规则请参看上一篇博客:从单例模式到Happens-Before),保证了在赋值操作(临时变量赋值给单例变量)之前对象的初始化一定完成了。为什么?

重点来了

因为这段代码在同步代码块中,所以保证了只有一条线程串行执行这几行代码,所以同时满足了线程内表现为串行的语义程序次序规则

  1. 在线程内表现为串行的语义中,JVM保证了临时变量调用toString方法(或任意方法)时,该对象一定初始化完成了!请思考体会一下表现为串行的含义。
  2. 而程序次序规则又保证了DCL temp = new DCL()Happens-Beforeinstance = temp,即前面的赋值操作一定对后面这个赋值操作可见

总结

通过线程内表现为串行的语义程序次序规则这两条规则的叠加使用,我们做到了在不使用volatile关键字修饰的情况下DCL为线程安全。

通过这个例子可见,只要掌握了分析多线程安全的要点,找到原因,我们也可以在解决问题时提出不一样的解决方案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值