Double-check

这样子是只能在单线程中运行:

// Single-threaded version
class Foo {
    private static Helper helper;
    public Helper getHelper() {
        if (helper == null) {
            helper = new Helper();
        }
        return helper;
    }

    // other functions and members...
}

多线程中,当然是不行的了。

全部加个锁是可以的

// Correct but possibly expensive multithreaded version
class Foo {
    private Helper helper;
    public synchronized Helper getHelper() {
        if (helper == null) {
            helper = new Helper();
        }
        return helper;
    }

    // other functions and members...
}

但是会很慢,只有一开始需要锁,当已经构造完成并且将reference赋值给了helper后,就不需要再synchronize了。

部分加个锁

// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
    private Helper helper;
    public Helper getHelper() {
        if (helper == null) {
            synchronized (this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }

    // other functions and members...
}

这就是典型的错误了,这里helper = new Helper();这句话,本意是创建一个Helper实例,并且将这个实例的引用赋值给helper变量。有可能会被JIT(也就是JVM的优化器)优化。然后构造函数变成内联函数。如果对于构造函数的引用被内联化,那么共享变量有可能会在内存分配的时候就更新,优先于构造函数开始构造这个实例。也就是说:这里的将地址值赋值给helper变量,可能会优先于Helper实例的构造。也就是被reorder了。

加两个锁


// (Still) Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo { 
  private Helper helper = null;
  public Helper getHelper() {
    if (helper == null) {
      Helper h;
      synchronized(this) {
        h = helper;
        if (h == null) 
            synchronized (this) {
              h = new Helper();
            } // release inner synchronization lock
        helper = h;
        } 
      }    
    return helper;
    }
  // other functions and members...
  }

这样子也是不行的,因为monitorexit(也就是释放锁)的规则,只保证了释放锁之前的操作一定要被执行,没有说要被完成。也就导致还是可能会使helper指向的对象时一个未完全构造的对象。

加个volatile

// Works with acquire/release semantics for volatile in Java 1.5 and later
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
    private volatile Helper helper;
    public Helper getHelper() {
        Helper localRef = helper;
        if (localRef == null) {
            synchronized (this) {
                localRef = helper;
                if (localRef == null) {
                    helper = localRef = new Helper();
                }
            }
        }
        return localRef;
    }

    // other functions and members...
}

那么我们添加了volatile变量,就阻止了这个reorder
volatile变量保证,any actions before the write of volatile, must before the write of volatile。也就是说本来在我们volatile变量的写操作之前的操作,被优化时,不会跑到volatile变量的写操作后面。对于这个问题,就是说:对Helper实例的构造,不会跑到将地址值赋值到helper变量之前

参考

doublecheck
wiki
什么地方乱序了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值