简体   繁体   中英

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?

I'm asking this since Set documentation states that it's not, but I'm only performing read operations.

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.

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.

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() .

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.

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.

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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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