Java HashSet 是线程安全的只读吗?
如果我通过 Collections.unmodifyingSet() 运行 HashSet 实例后,它是线程安全的吗?
我问这个是因为 Set 文档声明它不是,但我只是执行读取操作。
If I have an instance of an HashSet after I ran it through Collections.unmodifiableSet(), is it thread-safe?
I'm asking this since Set documentation states that it's not, but I'm only performing read operations.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
来自 Javadoc:
读取不会修改集,因此没问题。
From the Javadoc:
Reading doesn't modify a set, therefore you're fine.
如果以只读方式使用,
HashSet
将是线程安全的。这并不意味着您传递给Collections.unmodifyingSet()
的任何 Set 都是线程安全的。想象一下这个
contains
的简单实现,它缓存了最后检查的值:显然这不是线程安全的。
HashSet
will be threadsafe if used in a read-only manner. That doesn't mean that any Set that you pass toCollections.unmodifiableSet()
will be threadsafe.Imagine this naive implementation of
contains
that caches the last value checked:Clearly this wouldn't be threadsafe.
它是线程安全的,但只是由于
Collections.unmodifyingSet()
在内部以安全的方式发布了目标Set
(通过final
> 字段)。请注意,一般来说,诸如“只读对象始终是线程安全的”之类的陈述是不正确的,因为它们没有考虑操作重新排序的可能性。
(理论上)有可能,由于对于操作重新排序,在对象完全初始化并填充数据之前,对该只读对象的引用将对其他线程可见。为了消除这种可能性,您需要以安全的方式发布对对象的引用,例如,将它们存储在
final
字段中,就像通过Collections.unmodifyingSet()
完成的那样。It would be thread safe, but only owing to the fact that
Collections.unmodifiableSet()
internally publishes the targetSet
in safe manner (via thefinal
field).Note that in general statements such as "read-only objects are always thread-safe" are not correct, since they don't take into account possibility of operation reordering.
It's (theoretically) possible that, due to operation reordering, a reference to that read-only object will become visible to other threads before object is completely initialized and populated with data. To eliminate this possibility you need to publish references to the object in safe manner, for example, by storing them in
final
fields, as it's done byCollections.unmodifiableSet()
.如果不改变每个数据结构,那么它都是线程安全的。
因为必须改变 HashSet 才能初始化它,所以有必要在初始化该集合的线程和所有读取线程之间同步一次。您只需执行一次一次。例如,当您将对不可修改集的引用传递给以前从未接触过它的新线程时。
Every data structure is thread-safe if you don't mutate it.
Because you have to mutate a HashSet in order to initialize it, it is necessary to synchronize once between the thread which initialized the set and all reading threads. You have to do it only one time. For example when you pass the reference to the unmodifiable set to a new thread which never touched it before.
我不认为它是线程安全的,只是因为你运行 Collections.unmodifyingSet() 。即使 HashSet 已完全初始化并且您将其标记为不可修改,也不意味着这些更改将对其他线程可见。更糟糕的是,在没有同步的情况下,编译器可以对指令重新排序,这可能意味着读取线程不仅会看到丢失的数据,而且还会看到处于奇怪状态的哈希集。因此,您将需要一些同步。我相信解决这个问题的一种方法是将哈希集创建为最终的,并在构造函数中完全初始化它。这是一篇关于 JMM 的好文章 http:// www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html。阅读有关最终字段在新 JMM 下如何工作的部分?
能够查看字段的正确构造值固然很好,但如果字段本身是引用,那么您还希望代码能够查看它指向的对象(或数组)的最新值。如果你的字段是最终字段,这也是有保证的。因此,您可以拥有指向数组的最终指针,而不必担心其他线程看到数组引用的正确值,但看到数组内容的错误值。同样,这里的“正确”是指“截至对象构造函数结束时最新的”,而不是“最新可用的值”。
I don't believe it is thread safe just because you run Collections.unmodifiableSet(). Even though the HashSet if fully initialized and you marked it as unmodifiable, doesn't mean that those changes will be visible to other threads. Even worse, in the absence of synchronization, a compilier is allowed to re-order instructions, which could mean that not only does a reading thread see missing data but it can also see the hashset in a wierd state. Therefore you will need some synchronization. I believe one way around this is to create the hashset as final and to fully initialize it in the constructor. Here is a good article on the JMM http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html. Read the section on How do final fields work under the new JMM?
The ability to see the correctly constructed value for the field is nice, but if the field itself is a reference, then you also want your code to see the up to date values for the object (or array) to which it points. If your field is a final field, this is also guaranteed. So, you can have a final pointer to an array and not have to worry about other threads seeing the correct values for the array reference, but incorrect values for the contents of the array. Again, by "correct" here, we mean "up to date as of the end of the object's constructor", not "the latest value available".
是的,并发读取访问是安全的。这是文档中的相关句子:
它指出,仅当
至少一个
线程修改它时才需要同步。来源: https://docs.oracle.com/ javase/8/docs/api/java/util/HashSet.html
Yes, it is safe for concurrent read access. Here is the relevant sentence from the documentation:
It states that you only need to synchronize if
at least one
thread modifies it.Source: https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html
如果共享内存永远不会改变,那么您可以随时读取而无需同步。使集合不可修改只会强制执行无法写入的事实。
If the shared memory will never be changed, you can always read without synchronizing. Making the set unmodifiable will just enforce the fact that no writes can be made.