看起来Java内存模型没有定义本地缓存的“刷新”和“刷新”,相反人们只是为了简单起见才这样称呼它,但实际上“发生在之前”的关系意味着以某种方式刷新和刷新(如果您可以解释这一点,但不是问题的直接部分)。
再加上JLS中关于 Java 内存模型的部分并没有以易于理解的方式编写,这让我感到非常困惑。
因此,您能否告诉我我在以下代码中所做的假设是否正确,因此是否可以保证正确运行?
它部分基于 Wikipedia article on Double-checked locking中提供的代码,但是作者使用了一个包装类 ( FinalWrapper
),但其原因对我来说并不完全清楚。也许是为了支持null
价值观?
public class Memoized<T> {
private T value;
private volatile boolean _volatile;
private final Supplier<T> supplier;
public Memoized(Supplier<T> supplier) {
this.supplier = supplier;
}
public T get() {
/* Apparently have to use local variable here, otherwise return might use older value
* see https://jeremymanson.blogspot.com/2008/12/benign-data-races-in-java.html
*/
T tempValue = value;
if (tempValue == null) {
// Refresh
if (_volatile);
tempValue = value;
if (tempValue == null) {
// Entering refreshes, or have to use `if (_volatile)` again?
synchronized (this) {
tempValue = value;
if (tempValue == null) {
value = tempValue = supplier.get();
}
/*
* Exit should flush changes
* "Flushing" does not actually exists, maybe have to use
* `_volatile = true` instead to establish happens-before?
*/
}
}
}
return tempValue;
}
}
我还读到构造函数调用可以内联和重新排序,从而导致对未初始化对象的引用(请参阅博客上的此评论)。那么直接分配供应商的结果是否安全,还是必须分两步完成?
value = tempValue = supplier.get();
两步:
tempValue = supplier.get();
// Reorder barrier, maybe not needed?
if (_volatile);
value = tempValue;
编辑:这个问题的标题有点误导,目的是减少 volatile 字段的使用。如果初始化值已经在线程的缓存中,则value
直接访问,无需再次查看主存。