简体   繁体   English

是Java HashSet线程安全的只读?

[英]is Java HashSet thread-safe for read only?

If I have an instance of an HashSet after I ran it through Collections.unmodifiableSet(), is it thread-safe? 如果我在通过Collections.unmodifiableSet()运行它后有一个HashSet实例,它是否是线程安全的?

I'm asking this since Set documentation states that it's not, but I'm only performing read operations. 我问这个,因为Set文档声明它不是,但我只是执行读操作。

From the Javadoc: 来自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. 阅读不会修改一套,所以你很好。

HashSet will be threadsafe if used in a read-only manner. 如果以只读方式使用, HashSet将是线程安全的。 That doesn't mean that any Set that you pass to Collections.unmodifiableSet() will be threadsafe. 这并不意味着您传递给Collections.unmodifiableSet() 任何 Set都将是线程安全的。

Imagine this naive implementation of contains that caches the last value checked: 想象一下这个contains天真实现缓存最后检查的值:

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. 显然,这不是线程安全的。

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). 这将是线程安全的,但仅仅是因为Collections.unmodifiableSet()内部以安全的方式(通过final字段)发布目标Set

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() . 要消除这种可能性,您需要以安全的方式发布对对象的引用,例如,通过将它们存储在final字段中,就像Collections.unmodifiableSet()

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. 因为你必须改变HashSet才能初始化它,所以必须在初始化集合的线程和所有读取线程之间进行一次同步。 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. 例如,当您将对不可修改集的引用传递给之前从未触及它的新线程时。

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. 它声明如果at least one线程修改它,您只需要同步。

I don't believe it is thread safe just because you run Collections.unmodifiableSet(). 我不相信它是线程安全的只是因为你运行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. 即使HashSet完全初始化并且您将其标记为不可修改,也不意味着其他线程可以看到这些更改。 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. 我相信解决这个问题的一种方法是将hashset创建为final并在构造函数中完全初始化它。 Here is a good article on the JMM http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html . 这是一篇关于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? 阅读有关新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". 同样,在这里“正确”,我们的意思是“对象的构造函数结束时的最新”,而不是“最新的可用值”。

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. 使集合不可修改只会强制执行不能进行写入的事实。

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

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