简体   繁体   English

Guava中使用的无锁延迟加载模式是否真的是线程安全的?

[英]Is the lockless lazy loading pattern used in Guava really thread-safe?

Some Guava internal types, like AbstractMultiset , have a pattern like this: 一些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();
}

The idea is to delay creating the collection views ( elementSet() , entrySet() ) until they're actually needed. 我们的想法是延迟创建集合视图( elementSet()entrySet() ),直到实际需要它们为止。 There's no locking around the process because if two threads call elementSet() at the same time, it's okay to return two different values. 进程周围没有锁定,因为如果两个线程同时调用elementSet() ,则可以返回两个不同的值。 There will be a race to write the elementSet field, but since writes to reference fields are always atomic in Java, it doesn't matter who wins the race. 将会有一场比赛来编写elementSet字段,但由于写入引用字段在Java中始终是原子的,因此谁赢得比赛并不重要。

However, I worry about what the Java memory model says about inlining here. 但是,我担心Java内存模型在这里内联的内容。 If createElementSet() and ElementSet 's constructor both get inlined, it seems like we could get something like this: 如果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;
}

This would allow another thread to observe a non-null, but incompletely initialized value for elementSet . 这将允许另一个线程观察到elementSet的非null但未完全初始化的值。 Is there a reason that can't happen? 有没有理由不能发生? From my reading of JLS 17.5 , it seems like other threads are only guaranteed to see correct values for final fields in elementSet , but since ElementSet ultimately derives from AbstractSet , I don't think there's a guarantee that all its fields are final . 从我阅读JLS 17.5开始 ,似乎其他线程只能保证在elementSet看到final字段的正确值,但由于ElementSet最终来自AbstractSet ,我认为并不能保证其所有字段都是final

I'm not 100% clear on this (I'm sure someone else on our team could answer this better). 我不是100%明确这一点(我确信我们团队中的其他人可以更好地回答这个问题)。 That said, a couple thoughts: 那说,有几个想法:

  1. I don't think we claim anywhere that this is (guaranteed to be) thread-safe. 我认为我们并不认为这是(确保)线程安全的。 Non-thread-safe collections such as HashMultiset extend AbstractMultiset . 非线程安全的集合(如HashMultiset扩展了AbstractMultiset That said, ConcurrentHashMultiset also extends AbstractMultiset and uses its implementation of elementSet() , so presumably it must in fact be possible for it to be thread-safe. 这就是说, ConcurrentHashMultiset还延伸AbstractMultiset并使用其实施的elementSet()所以想必一定实际上有可能为它是线程安全的。
  2. I believe the thread safety of this method is dependent on the implementation of createElementSet() . 我相信这个方法的线程安全性取决于createElementSet()的实现。 From what I can tell, if the Set created by createElementSet() is immutable (in that the fields that are assigned when it is constructed are final ), it should be thread-safe. 据我所知,如果createElementSet()创建的Set不可变的 (因为构造它时分配的字段是final ),它应该是线程安全的。 This appears to be true in the case of ConcurrentHashMultiset at least. 至少在ConcurrentHashMultiset的情况下似乎是这样。

Edit: I asked Jeremy Manson about this, and he said: "Your take on it seems fine to me. It isn't thread safe. If the object being constructed has all of the final fields in the right places, you should be fine, but I wouldn't rely on that by accident (note that many implementations are effectively immutable instead genuinely immutable)." 编辑:我向Jeremy Manson询问了这件事,他说:“你对它的看法对我来说似乎没问题。它不是线程安全的。如果正在构建的对象在正确的位置有所有的最终字段,你应该没问题,但我不会偶然依赖它(请注意,许多实现实际上是不可变的,而不是真正不可变的)。“

Note: For thread-safe collections like ConcurrentHashMultiset which use this pattern, objects created are intentionally and genuinely immutable (though if AbstractSet were to change, that could change, as Chris noted in the comments). 注意:对于使用此模式的ConcurrentHashMultiset等线程安全集合,创建的对象是有意且真正不可变的(但如果AbstractSet要更改,那可能会发生变化,正如Chris在评论中指出的那样)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM