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 bar开发者_如何学Pythonriered"?
Tanks! Tom
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.
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.
I think the best answer here is that volatile
is not a way to ensure thread-safety.
Using ConcurrentHashMap
is pretty much all you need. Yes, make the reference to the top-level Map
final
if you can, but volatile
is not necessary in any event. The second-level Map
reference inside are the business of ConcurrentHashMap
to get right, and one assumes it does.
精彩评论