only one thread will call reload() at a time. I want to ensure that at any time, callers of get will see the content of the same latest map instance.
Pardon me for my vague descrition on requirement. I want to make sure that, whenever a thread call reload, all the readers will get the mapping entry from a valid map(both the reference and the inner structure is valid) instance, ethier the old or the new map instance is OK. But if get return a mapping from a corruptted map structure, not OK.
For initialization, assume MapCache.map is safely initialized somehow.
Is make instance variable map volatile enough to guarantee that?
public class MapCache {
private volatile Map<String,String> map;
public void reload() {
this.map = newMap();
}
public String get(String key) {
return map.get(key);
}
private Map<String,String> newMap(){
Map<String, String> localMap = new HashMap<>();
//init localMap;
return localMap;
}
}
In Java Concurrency in Practice, on page 52, Safe publication idioms includes Storing a reference to it into a volatile field or AtomicReference; which means both the reference to the object and the object’s state can be made visible to other threads at the same time.
but on page 325,
The atomic array classes provide volatile access semantics to the elements of the array, a feature not available for ordinary arrays—a volatile array has volatile semantics only for the array reference, not for its elements.
a volatile array has volatile semantics only for the array reference, not for its elements. I don't quite understand this sentence. It seems in contradiction to the one on page 52. So when updating an volatile array, can we be sure that other thread can see the new array and array content?
HashMap use array as it's inner states, so is volatile HashMap enough for my requirement?
transient Node<K,V>[] table;
I searched the net and some saied volatile has hasppens-before effect(which is covered in Java Concurrency in Practice). It seems that every time a writer thread (say ThreadA) call reload() and update the volatile map reference, what the ThreadA sees the map inner structure will be visible to any reader thread which call get to access the volatile map reference. Is my understanding right? Some answers here says that even this is not guarrenteed.
Since the map is effectively immutable, so doesn't get updated after it is written to the MapCache.map, then there is no problem.
What is required is that there should not be a data race. You get a data race when you have 2 conflicting actions (the reading/writing of the MapCache.map field) that are not ordered by the happens-before relation.
There is a happens-before edge between the construction (so adding the items to the buckets) of the new filled map and the writing that map to the MapCache.map field. This is due to the program order rule.
When a read of the MapCache.map field sees a particular write to that field, then there is a happens-before edge between the write and the read of that field. This is due to the volatile variable rule.
There is a happens-before edge between loading the MyCache.map field and using it (e.g. calling map.get by accessing the buckets of the Map). This is due to the program order rule.
The happens-before relation is transitive, so the is a happens-before edge between creating/filling the map and reading the content of the map and as a consequence, there is no data-race.
Your concern about the array memory models semantics isn't relevant, since the map is effectively read-only. Only when you would write some concurrent HashMap, then you need to worry about the memory ordering effects of the items in the array.