首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >番石榴中使用的无锁惰性加载模式真的是线程安全吗?

番石榴中使用的无锁惰性加载模式真的是线程安全吗?
EN

Stack Overflow用户
提问于 2014-04-03 15:42:16
回答 1查看 565关注 0票数 15

有些番石榴内部类型,如AbstractMultiset,有如下模式:

代码语言:javascript
复制
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();
}

这样做的目的是推迟创建集合视图(elementSet()entrySet()),直到实际需要它们为止。进程周围没有锁定,因为如果两个线程同时调用elementSet(),则可以返回两个不同的值。编写elementSet字段会有一场竞赛,但是由于对引用字段的写入在Java中总是原子化的,所以谁赢得比赛并不重要。

但是,我担心Java内存模型在这里说的是内联。如果createElementSet()ElementSet的构造函数都是内联的,那么我们似乎可以得到如下内容:

代码语言:javascript
复制
@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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-04-03 17:20:41

我对此并不十分清楚(我相信我们团队中的其他人能更好地回答这个问题)。话虽如此,我有几个想法:

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

编辑:我问过杰里米·曼森这件事,他说:“你对它的看法对我来说很好。它不安全。如果正在构建的对象在正确的位置有所有最后的字段,你应该没事,但我不会偶然地依赖它(请注意,许多实现实际上是不可变的,而真正是不可变的)。”

注意:对于使用此模式的线程安全集合(如ConcurrentHashMultiset ),创建的对象是有意且真正不可变的(尽管AbstractSet要更改,这可能会改变,正如克里斯在注释中所指出的那样)。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22842588

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档