通过 final 关键字来实现 双重检查(DCL) 时,为什么 局部变量 是必须的?
在 双重检查锁定(DCL)的wiki 中,对于 DCL提供了一种通过 final 关键字来实现的方式,源码如下:
public class FinalWrapper<T> {
public final T value;
public FinalWrapper(T value) {
this.value = value;
}
}
public class Foo {
private FinalWrapper<Helper> helperWrapper;
public Helper getHelper() {
FinalWrapper<Helper> wrapper = helperWrapper;
if (wrapper == null) {
synchronized(this) {
if (helperWrapper == null) {
helperWrapper = new FinalWrapper<Helper>(new Helper());
}
wrapper = helperWrapper;
}
}
return wrapper.value;
}
}
同时,文章中有如下说明:
The local variable wrapper is required for correctness.
请问一下,为什么这个 局部变量 wrapper 是必须的?
我个人认为此变量是可有可无的,还请有识者指点一下。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
第一次看到双重锁定检查还可以用final来实现,我试着回答一下这个问题,说一下自己的理解。
首先说一下基于volatile实现的双重锁定检查:
如果instance不声明为volatile类型的,上面代码中的双重锁定检查在多线程环境下是会有问题的。
主要问题在于多线程环境下,在一个线程中instance有可能在没有完全初始化之前(只初始化了一部分),就被其他线程使用了。 比如,instance刚被分配了内存空间,还没完成new Instance()的全部操作,其他线程在第一次对instance进行是否为空的判断的时候,结果是false(由于instance指向的是个内存地址,所以分配了内存空间之后,instance这个变量已经不为空),这个时候这个线程就会直接返回,然而instance变量指向的内存空间还没完成new Instance()的全部操作。 这样一来,一个没有被完全初始化的instance就会被使用。
上面我说的这种状况看起来不可能发生,毕竟从代码来看,这根本不可能啊。
初始化instance变量的伪代码如下所示:
之所以会发生上面我说的这种状况,是因为在一些编译器上存在指令排序,初始化过程可能被重排成这样:
而volatile存在的意义就在于禁止这种重排!要想详细了解一下指令重排,可以参考一下这里
再来说一下final域的重排规则:
写final的重排规则:
JMM禁止编译器把final域的写重排序到构造函数之外。
编译器会在final域的写之后,构造函数return之前,插入一个StoreStore屏障。这个屏障禁止处理器把final域的写重排序到构造函数之外。
也就是说:写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了。
读final的重排规则:
在一个线程中,初次读对象引用与初次读该对象包含的final域,JMM禁止处理器重排序这两个操作(注意,这个规则仅仅针对处理器)。编译器会在读final域操作的前面插入一个LoadLoad屏障。
也就是说:读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读包含这个final域的对象的引用。
如果final域是引用类型,那么增加如下约束:
在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
(个人觉得基本意思也就是确保在构造函数外把这个被构造对象的引用赋值给一个引用变量之前,final域已经完全初始化并且赋值给了当前构造对象的成员域,至于初始化和赋值这两个操作则不确保先后顺序。)
具体解释可以参考这里
再回到这个问题本身:
主要问题在于这些关键字对指令重排的保证,个人的一个理解是这样的,欢迎讨论。
@rainbow702 @FrancisZ 这个问题我最近也看了,请教一下二位,你们最终的结论是:
The local variable wrapper is required for correctness. 是完全不需要的
对吗?