gpt4 book ai didi

Java 惰性线程安全单例,使用 Final 字段实现

转载 作者:行者123 更新时间:2023-12-02 02:32:30 24 4
gpt4 key购买 nike

我不明白为什么这里需要局部变量:

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> tempWrapper = helperWrapper;

if (tempWrapper == null) {
synchronized(this) {
if (helperWrapper == null) {
helperWrapper = new FinalWrapper<Helper>(new Helper());
}
tempWrapper = helperWrapper;
}
}
return tempWrapper.value;
}
}

我从以下位置获取此代码:https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java 。如果没有这个局部变量,我们会遇到什么问题?根据维基文章:

Semantics of final field in Java 5 can be employed to safely publish the helper object without using volatile. The local variable tempWrapper is required for correctness: simply using helperWrapper for both null checks and the return statement could fail due to read reordering allowed under the Java Memory Model. Performance of this implementation is not necessarily better than the volatile implementation.

提前致谢。

最佳答案

为了了解根本问题,让我们从代码中删除局部变量:

public class Foo {
private FinalWrapper<Helper> helperWrapper;

public Helper getHelper() {
if (helperWrapper == null) {
synchronized(this) {
if (helperWrapper == null) {
helperWrapper = new FinalWrapper<Helper>(new Helper());
}
}
}
return helperWrapper.value;
}
}

在这种情况下我们有三个读取:

  1. 外部 null 检查。
  2. 内部 null 检查。
  3. 返回前的读取。

问题是,由于读取重新排序,第一次读取可能返回非空值,而第三次读取可能返回null。这意味着第三次读取发生在第一次读取之前,这应该确保 helperWrapper 已初始化...

添加局部变量可以解决问题,因为我们将 helperWrapper 值分配给 tempWrapper,然后读取 tempWrapper 的顺序并不重要。如果它具有非空值,则它既用于空检查又用于返回语句。

这种情况之所以会发生,是因为 Java 内存模型允许出于优化目的而对操作进行此类重新排序。看看 here 的引用:

What is meant by reordering?

There are a number of cases in which accesses to program variables (object instance fields, class static fields, and array elements) may appear to execute in a different order than was specified by the program. The compiler is free to take liberties with the ordering of instructions in the name of optimization. Processors may execute instructions out of order under certain circumstances. Data may be moved between registers, processor caches, and main memory in different order than specified by the program.

[...]

The compiler, runtime, and hardware are supposed to conspire to create the illusion of as-if-serial semantics, which means that in a single-threaded program, the program should not be able to observe the effects of reorderings. However, reorderings can come into play in incorrectly synchronized multithreaded programs, where one thread is able to observe the effects of other threads, and may be able to detect that variable accesses become visible to other threads in a different order than executed or specified in the program.

[...]

关于Java 惰性线程安全单例,使用 Final 字段实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46867893/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com