doublecheck单例由于指令重排产生的问题

转自https://blog.csdn.net/anjxue/article/details/51038466

在阅读之前,请先了解下线程并发涉及到的三个概念:原子性、可见性、有序性,可以看下这篇文章:http://www.cnblogs.com/dolphin0520/p/3920373.html

我假设你已经看过几篇double check的文章,但还是一知半解。

我们先看这种双重检查,不加volatile

public static Singleton instance;
public static Singleton getInstance()
{
  if (instance == null)              //1
  {                                  //2
    synchronized(Singleton.class) {  //3
      if (instance == null)          //4
        instance = new Singleton();  //5
    }
  }
  return instance;
}

这种方式存在什么问题呢? 
也许有人说存在可见性问题:线程1执行完第5步,释放锁。线程2获得锁后执行到第4步,由于可见性的原因,发现instance还是null,从而初始化了两次。 
但是不会存在这种情况,因为synchronized能保证线程1在释放锁之前会讲对变量的修改刷新到主存当中,线程2拿到的值是最新的。

实际存在的问题是无序性。 
第5步这个new操作是无序的,它可能会被编译成: 
- a. 先分配内存,让instance指向这块内存 
- b. 在内存中创建对象

然而我们需要意识到这么一个问题,synchronized虽然是互斥的,但不代表一次就把整个过程执行完,它在中间是可能释放时间片的,时间片不是锁。(我因为这里没转过来,耽误了很多时间) 
也就是说可能在a执行完后,时间片被释放,线程2执行到1,这时他读到的instance是不是null呢?(标记1) 
基于可见性,可能是null,也可能不是null。 
非常奇葩的是,在这个例子中,如果读到的是null,反而没问题了,接下来会等待锁,然后再次判断时不为null,最后返回单例。 
如果读到的不是null,那么坏了,按逻辑它就直接return instance了,这个instance还没执行构造参数,去做事情的话,很可能就崩溃了。


加volatile

public volatile static Singleton instance;
public static Singleton getInstance()
{
  if (instance == null)              //1
  {                                  //2
    synchronized(Singleton.class) {  //3
      if (instance == null)          //4
        instance = new Singleton();  //5
    }
  }
  return instance;
}

唯一的区别是加了volatile关键字,那么会有什么现象? 
这时要区分jdk版本了,在jdk1.4及之前,volatile并不能保证new操作的有序性,但是它能保证可见性,因此标记1处,读到的不是null,导致了问题。 
从1.5开始,加了volatile关键字的引用,它的初始化就不能是: 
- a. 先分配内存,让instance指向这块内存 
- b. 在内存中创建对象

而应该是: 
- a.在内存中创建对象 
- b.让instance指向这个对象.

这种形式,也就避免了无序性问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值