检查(DCL) 时,为什么 形式参数是局部变量量 是必须的

404 Not Found
The requested URL /q/3541/a-9988 was not found on this server.在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
中,对于 DCL提供了一种通过 final 关键字来实现的方式,源码如下:
public class FinalWrapper&T& {
public final T
public FinalWrapper(T value) {
this.value =
public class Foo {
private FinalWrapper&Helper& helperW
public Helper getHelper() {
FinalWrapper&Helper& wrapper = helperW
if (wrapper == null) {
synchronized(this) {
if (helperWrapper == null) {
helperWrapper = new FinalWrapper&Helper&(new Helper());
wrapper = helperW
return wrapper.
同时,文章中有如下说明:The local variable wrapper is required for correctness.
请问一下,为什么这个 局部变量 wrapper 是必须的?我个人认为此变量是可有可无的,还请有识者指点一下。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
第一次看到双重锁定检查还可以用final来实现,我试着回答一下这个问题,说一下自己的理解。
首先说一下基于volatile实现的双重锁定检查:
public class SafeDoubleCheckedLocking {
private volatile static I
public static Instance getInstance() {
if (instance == null) {
synchronized (SafeDoubleCheckedLocking.class) {
if (instance == null)
instance = new Instance();//instance为volatile,现在没问题了
如果instance不声明为volatile类型的,上面代码中的双重锁定检查在多线程环境下是会有问题的。主要问题在于多线程环境下,在一个线程中instance有可能在没有完全初始化之前(只初始化了一部分),就被其他线程使用了。 比如,instance刚被分配了内存空间,还没完成new Instance()的全部操作,其他线程在第一次对instance进行是否为空的判断的时候,结果是false(由于instance指向的是个内存地址,所以分配了内存空间之后,instance这个变量已经不为空),这个时候这个线程就会直接返回,然而instance变量指向的内存空间还没完成new Instance()的全部操作。 这样一来,一个没有被完全初始化的instance就会被使用。
上面我说的这种状况看起来不可能发生,毕竟从代码来看,这根本不可能啊。
初始化instance变量的伪代码如下所示:
memory = allocate();
//1:分配对象的内存空间
ctorInstance(memory);
//2:初始化对象
instance =
//3:设置instance指向刚分配的内存地址
之所以会发生上面我说的这种状况,是因为在一些编译器上存在指令排序,初始化过程可能被重排成这样:
memory = allocate();
//1:分配对象的内存空间
instance =
//3:设置instance指向刚分配的内存地址
//注意,此时对象还没有被初始化!
ctorInstance(memory);
//2:初始化对象
而volatile存在的意义就在于禁止这种重排!要想详细了解一下指令重排,可以参考一下
再来说一下final域的重排规则:
写final的重排规则:
JMM禁止编译器把final域的写重排序到构造函数之外。
编译器会在final域的写之后,构造函数return之前,插入一个StoreStore屏障。这个屏障禁止处理器把final域的写重排序到构造函数之外。也就是说:写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了。
读final的重排规则:
在一个线程中,初次读对象引用与初次读该对象包含的final域,JMM禁止处理器重排序这两个操作(注意,这个规则仅仅针对处理器)。编译器会在读final域操作的前面插入一个LoadLoad屏障。也就是说:读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读包含这个final域的对象的引用。
如果final域是引用类型,那么增加如下约束:
在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。(个人觉得基本意思也就是确保在构造函数外把这个被构造对象的引用赋值给一个引用变量之前,final域已经完全初始化并且赋值给了当前构造对象的成员域,至于初始化和赋值这两个操作则不确保先后顺序。)
具体解释可以参考
再回到这个问题本身:
public class Foo {
private FinalWrapper&Helper& helperW
public Helper getHelper() {
FinalWrapper&Helper& wrapper = helperW
if (wrapper == null) {
synchronized(this) {
if (helperWrapper == null) {
helperWrapper = new FinalWrapper&Helper&(new Helper());
wrapper = helperW//此处增加了对helperWrapper的引用,写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了。
//所以此处增加一次对helperWrapper的引用,可以确保FinalWrapper中的value已经成功初始化。
//如果此处没有赋值给本地变量,final域的写操作保证不能生效,那么getHelper()方法并不能保证,被return的value已经被正确初始化过。
return wrapper.
//读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读包含这个final域的对象的引用。所以此处的读取操作确保读value之前,会读取wrapper这个引用变量指向的对象,
//而写操作又可以确保在对象可见之前,final域已经正确初始化了。所以此处肯定会读取到正确的value。
//如果读final域不做这个保证,那么对value的读取操作有可能会被重排到对wrapper赋值之前,有可能读取到不正确的value值。
主要问题在于这些关键字对指令重排的保证,个人的一个理解是这样的,欢迎讨论。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
这个问题我最近也看了,请教一下二位,你们最终的结论是:The local variable wrapper is required for correctness. 是完全不需要的对吗?
该答案已被忽略,原因:不符合答题规范:内容不是答案,可用评论、投票替代
同步到新浪微博
分享到微博?
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。404 Not Found
The requested URL /q/3541 was not found on this server.404 Not Found
The requested URL /q/3541 was not found on this server.404 Not Found
The requested URL /q/3541 was not found on this server.}

我要回帖

更多关于 js 局部变量 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信