【发布时间】:2019-06-03 14:56:41
【问题描述】:
看起来 Java 内存模型没有定义本地缓存的“刷新”和“刷新”,相反人们只是为了简单起见才这样称呼它,但实际上“发生前”关系意味着刷新和刷新以某种方式(会如果您能解释这一点,那就太好了,但不是问题的直接部分)。
这让我很困惑,再加上关于Java Memory Model in the JLS 的部分并不是以易于理解的方式编写的。
因此,请您告诉我我在以下代码中所做的假设是否正确,因此是否可以保证正确运行?
它部分基于Double-checked locking 上的 Wikipedia 文章中提供的代码,但是作者使用了包装类 (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;
}
}
我还读到构造函数调用可以被内联和重新排序,从而导致对未初始化对象的引用(参见this comment on a blog)。那么直接分配供应商的结果是否安全,还是必须分两步完成?
value = tempValue = supplier.get();
两步:
tempValue = supplier.get();
// Reorder barrier, maybe not needed?
if (_volatile);
value = tempValue;
编辑:这个问题的标题有点误导,目的是减少 volatile 字段的使用。如果初始化值已经在某个线程的缓存中,则直接访问value,无需再去主存中查找。
【问题讨论】:
-
你的 volatile 没有用,因为你没有给它赋值。 “刷新”发生在
synchronized块的末尾,因为根据 JMM,锁的释放发生在随后获取同一锁之前 -
final的双重检查锁定示例简要说明了here on Stack Overflow(也由 Aleksey Shipilev 撰写)。 -
"你的 volatile 没有用,因为你没有给它赋值。" @Ivan,我用它来创建发生前的关系,而不是在其中存储任何价值。
-
发生在我确定写入 volatile 和后续读取之间。您不会向该变量写入任何内容。
标签: java memoization lazy-initialization java-memory-model