Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Concurrency : Volatile vs final in "cascaded" variables?

is

final Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer, Map<String,Integer>>();
Map<Integer,Map<String,Integer>> statusInner = new ConcurrentHashMap<Integer, Map<String,Integer>>();
status.put(key,statusInner);

the same as

volatile Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer,   Map<String,Integer>>();
Map<Integer,Map<String,Integer>> statusInner = new ConcurrentHashMap<Integer, Map<String,Integer>>();
status.put(key,statusInner);

in case the inner Map is accessed by different Threads?

or is even something like this required:

volatile Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer, Map<String,Integer>>();
volatile Map<Integer,Map<String,Integer>> statusInner = new ConcurrentHashMap<Integer, Map<String,Integer>>();
status.put(key,statusInner);

In case the it is NOT a "cascaded" map, final and volatile have in the end the same effect of making shure that all threads see always the correct contents of the Map... But what happens if the Map iteself contains a map, as in the example... How do I make shure that the inner Map is correctly "Memory barriered"?

Tanks! Tom

like image 508
Tom Avatar asked Jun 03 '10 08:06

Tom


2 Answers

volatile only affects the ability of other threads to read the value of the variables it's attached to. It in no way affects the ability of another thread to see the keys and values of the map. For instance, I could have a volatile int[]. If I change the reference—i.e. if I change the actual array that it points to—other threads reading the array are guaranteed to see that change. However, if I change the third element of the array no such guarantees are made.

If status is final, the construction of the containing class creates a happens-before relationship with any subsequent reads, so they are able to see the value of status. Likewise any reads to your volatile variable are guaranteed to see the latest reference assignment to it. It's unlike you're swapping the actual map around very often, more like you're just changing keys and the overall map object stays as is.

For this question, then, we need to consult the documentation for ConcurrentHashMap:

Retrieval operations (including get) generally do not block, so may overlap with update operations (including put and remove). Retrievals reflect the results of the most recently completed update operations holding upon their onset.

This is kind of oddly worded, but the gist is that any get operation whose onset is after some put operation's return is guaranteed to see the results of that put. So you don't even need a volatile on the outer map; quoth the JLS:

A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

Summary

A final on the outer map is sufficient.

like image 186
jasonmp85 Avatar answered Oct 09 '22 07:10

jasonmp85


It's worth looking at Google-Collections and, in particular, MapMaker that lets you intelligently setup and create Maps. Being able to setup weak values, to enable better garbage collection, and expiration times, so you can use Maps for effective caching, is brilliant. As the Maps that MapMaker makes (:p) have the same properties as ConcurrentHashMap, you can be happy with its thread-safety.

final mapMaker = new MapMaker().weakValues(); //for convenience, assign
final Map<Integer,Map<String,Integer>> status = mapMaker.makeMap();
status.put(key, mapMaker.<String, Integer>makeMap());

Please note, you might want to look at your definition of statusInner, as it doesn't seem right.

like image 43
Ben Smith Avatar answered Oct 09 '22 09:10

Ben Smith