番石榴中使用的无锁延迟加载模式真的是线程安全的吗?
2022-09-03 15:45:27
一些番石榴内部类型,如 ,有这样的模式:AbstractMultiset
private transient Set<E> elementSet;
@Override
public Set<E> elementSet() {
Set<E> result = elementSet;
if (result == null) {
elementSet = result = createElementSet();
}
return result;
}
Set<E> createElementSet() {
return new ElementSet();
}
这个想法是延迟创建集合视图(,),直到实际需要它们。进程周围没有锁定,因为如果两个线程同时调用,则可以返回两个不同的值。编写字段将会有一场竞赛,但是由于写入引用字段在Java中始终是原子的,因此谁赢得比赛并不重要。elementSet()
entrySet()
elementSet()
elementSet
但是,我担心Java内存模型在这里对内联有什么看法。如果 和 的构造函数都内联,似乎我们可以得到这样的东西:createElementSet()
ElementSet
@Override
public Set<E> elementSet() {
Set<E> result = elementSet;
if (result == null) {
elementSet = result = (allocate an ElementSet);
(run ElementSet's constructor);
}
return result;
}
这将允许另一个线程观察 的非空值,但初始化不完全。有没有不发生的原因?从我对 JLS 17.5 的阅读来看,似乎其他线程只能保证看到 中字段的正确值,但由于最终派生自 ,我不认为可以保证它的所有字段都是 。elementSet
final
elementSet
ElementSet
AbstractSet
final