Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Java, is it safe to change a reference to a HashMap read concurrently

I hope this isn't too silly a question...

I have code similar to the following in my project:

public class ConfigStore {

    public static class Config {

        public final String setting1;
        public final String setting2;
        public final String setting3;

        public Config(String setting1, String setting2, String setting3) {
            this.setting1 = setting1;
            this.setting2 = setting2;
            this.setting3 = setting3;
        }

    }

    private volatile HashMap<String, Config> store = new HashMap<String, Config>();

    public void swapConfigs(HashMap<String, Config> newConfigs) {
        this.store = newConfigs;
    }

    public Config getConfig(String name) {
        return this.store.get(name);
    }

}

As requests are processed, each thread will request a config to use from the store using the getConfig() function. However, periodically (every few days most likely), the configs are updated and swapped out using the swapConfigs() function. The code that calls swapConfigs() does not keep a reference to the Map it passes in as it is simply the result of parsing a configuration file.

  • In this case, is the volatile keyword still needed on the store instance variable?
  • Will the volatile keyword introduce any potential performance bottlenecks that I should be aware of or can avoid given that the rate of reads greatly exceeds the rate of writes?

Thanks very much,

like image 226
Midpoint Avatar asked Aug 25 '11 07:08

Midpoint


People also ask

Is ConcurrentHashMap thread-safe?

ConcurrentHashMap class is thread-safe i.e. multiple threads can operate on a single object without any complications.

How can be a HashMap made concurrent thread-safe?

Concurrent. ConcurrentHashMap class achieves thread-safety by dividing the map into segments, the lock is required not for the entire object but for one segment, i.e one thread requires a lock of one segment. In ConcurrentHashap the read operation doesn't require any lock.

Can a HashMap be accessed concurrently?

If you need to access and update a map from multiple threads, ConcurrentHashMap is the best option. It provides all the operations of a HashMap and additionally allows concurrent access for read, write, and update.

Can multiple threads read ConcurrentHashMap at the same time?

Yes. When thread locks one segment for updation it does not block it for retrieval (done by get method) hence some other thread can read the segment (by get method), but it will be able to read the data before locking.


2 Answers

Since changing references is an atomic operation, you won't end up with one thread modifying the reference, and the other seeing a garbage reference, even if you drop volatile. However, the new map may not get instantly visible for some threads, which may consequently keep reading configuration from the old map for an indefinite time (or forever). So keep volatile.

Update

As @BeeOnRope pointed out in a comment below, there is an even stronger reason to use volatile:

"non-volatile writes [...] don't establish a happens-before relationship between the write and subsequent reads that see the written value. This means that a thread can see a new map published through the instance variable, but this new map hasn't been fully constructed yet. This is not intuitive, but it's a consequence of the memory model, and it happens in the real word. For an object to be safely published, it must be written to a volatile, or use a handful of other techniques.

Since you change the value very rarely, I don't think volatile would cause any noticeable performance difference. But at any rate, correct behaviour trumps performance.

like image 99
Péter Török Avatar answered Oct 13 '22 01:10

Péter Török


No, this is not thread safe without volatile, even apart from the issues of seeing stale values. Even though there are no writes to the map itself, and reference assignment is atomic, the new Map<> has not been safely published.

For an object to be safely published, it must be communicated to other threads using some mechanism that either establishes a happens-before relationship between the object construction, the reference publication and the reference read, or it must use a handful of narrower methods which are guaranteed to be safe for publishing:

  • Initializing an object reference from a static initializer.
  • Storing a reference to it into a final field.

Neither of those two publication specific ways applies to you, so you'll need volatile to establish happens-before.

Here is a longer version of this reasoning, including links to the JLS and some examples of real-world things that can happen if you don't publish safely.

More details on safe publication can be found in JCIP (highly recommended), or here.

like image 35
BeeOnRope Avatar answered Oct 12 '22 23:10

BeeOnRope