Java HashSet 是线程安全的只读吗?

发布于 2024-10-25 01:52:12 字数 106 浏览 2 评论 0原文

如果我通过 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

思念满溢 2024-11-01 01:52:12

来自 Javadoc:

请注意,此实现不是同步的。如果多个线程同时访问一个哈希集,并且至少有一个线程修改了该集,则必须进行外部同步

读取不会修改集,因此没问题。

From the Javadoc:

Note that this implementation is not synchronized. If multiple threads access a hash set concurrently, and at least one of the threads modifies the set, it must be synchronized externally

Reading doesn't modify a set, therefore you're fine.

风向决定发型 2024-11-01 01:52:12

如果以只读方式使用,HashSet 将是线程安全的。这并不意味着您传递给 Collections.unmodifyingSet()任何 Set 都是线程安全的。

想象一下这个 contains 的简单实现,它缓存了最后检查的值:

Object lastKey;
boolean lastContains;

public boolean contains(Object key) {
   if ( key == lastKey ) {
      return lastContains;
   } else {
      lastKey = key;
      lastContains = doContains(key);
      return lastContains;
   }
}

显然这不是线程安全的。

HashSet will be threadsafe if used in a read-only manner. That doesn't mean that any Set that you pass to Collections.unmodifiableSet() will be threadsafe.

Imagine this naive implementation of contains that caches the last value checked:

Object lastKey;
boolean lastContains;

public boolean contains(Object key) {
   if ( key == lastKey ) {
      return lastContains;
   } else {
      lastKey = key;
      lastContains = doContains(key);
      return lastContains;
   }
}

Clearly this wouldn't be threadsafe.

┊风居住的梦幻卍 2024-11-01 01:52:12

它是线程安全的,但只是由于 Collections.unmodifyingSet() 在内部以安全的方式发布了目标 Set(通过 final > 字段)。

请注意,一般来说,诸如“只读对象始终是线程安全的”之类的陈述是不正确的,因为它们没有考虑操作重新排序的可能性。

(理论上)有可能,由于对于操作重新排序,在对象完全初始化并填充数据之前,对该只读对象的引用将对其他线程可见。为了消除这种可能性,您需要以安全的方式发布对对象的引用,例如,将它们存储在 final 字段中,就像通过 Collections.unmodifyingSet() 完成的那样。

It would be thread safe, but only owing to the fact that Collections.unmodifiableSet() internally publishes the target Set in safe manner (via the final 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 by Collections.unmodifiableSet().

吾性傲以野 2024-11-01 01:52:12

如果不改变每个数据结构,那么它都是线程安全的。

因为必须改变 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.

梦萦几度 2024-11-01 01:52:12

我不认为它是线程安全的,只是因为你运行 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".

樱娆 2024-11-01 01:52:12

是的,并发读取访问是安全的。这是文档中的相关句子:

如果多个线程同时访问一个哈希集,并且至少有一个线程修改了该集,则必须进行外部同步。

它指出,仅当至少一个线程修改它时才需要同步。

来源: 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:

If multiple threads access a hash set concurrently, and at least one of the threads modifies the set, it must be synchronized externally.

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

流殇 2024-11-01 01:52:12

如果共享内存永远不会改变,那么您可以随时读取而无需同步。使集合不可修改只会强制执行无法写入的事实。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文