gpt4 book ai didi

java - Guava 中使用的无锁懒加载模式真的是线程安全的吗?

转载 作者:太空狗 更新时间:2023-10-29 22:57:55 24 4
gpt4 key购买 nike

某些 Guava 内部类型,如 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();
}

想法是延迟创建 Collection View (elementSet()entrySet()),直到真正需要它们为止。进程周围没有锁定,因为如果两个线程同时调用 elementSet(),则可以返回两个不同的值。将会有一场编写 elementSet 字段的竞赛,但由于在 Java 中对引用字段的写入始终是原子的,因此谁赢得比赛并不重要。

但是,我担心 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;
}

这将允许另一个线程观察 elementSet 的非空但未完全初始化的值。有不能发生的原因吗?从我阅读 JLS 17.5 ,似乎其他线程只能保证在 elementSet 中看到 final 字段的正确值,但由于 ElementSet 最终派生自 AbstractSet ,我认为不能保证它的所有字段都是 final

最佳答案

我不是 100% 清楚这一点(我相信我们团队中的其他人可以更好地回答这个问题)。也就是说,一些想法:

  1. 我不认为我们在任何地方声称这是(保证)线程安全的。 HashMultiset 等非线程安全集合扩展了 AbstractMultiset。也就是说,ConcurrentHashMultiset 还扩展了 AbstractMultiset 并使用了它的 elementSet() 实现,因此据推测它实际上必须可能 是线程安全的。
  2. 我相信此方法的线程安全性取决于 createElementSet() 的实现。据我所知,如果 createElementSet() 创建的 Set不可变的(因为在构造时分配的字段是final),它应该是线程安全的。至少在 ConcurrentHashMultiset 的情况下,这似乎是正确的。

编辑:我问过 Jeremy Manson 这件事,他说:“你的看法对我来说似乎很好。它不是线程安全的。如果正在构造的对象具有所有最终字段在正确的地方,你应该没问题,但我不会意外地依赖它(请注意,许多实现实际上是不可变的,而不是真正不可变的)。”

注意:对于像 ConcurrentHashMultiset 这样使用这种模式的线程安全集合,创建的对象是有意且真正不可变的(尽管如果 AbstractSet 发生变化,那可能会发生变化,正如克里斯在评论中指出的那样)。

关于java - Guava 中使用的无锁懒加载模式真的是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22842588/

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