Reputation: 7523
Consider the code snippet
class A {
private Map<String, Object> taskMap = new HashMap<>();
private volatile Object[] tasksArray ;
// assume this happens on thread1
public void assignTasks() {
synchronized(taskMap){
// put new tasks into map
// reassign values from map as a new array to tasksArray ( for direct random access )
}
}
// assume this is invoked on Thread2
public void action(){
int someindex = <init index to some value within tasksArray.length>;
Object[] localTasksArray = tasksArray;
Object oneTask = localTasksArray[someindex];
// Question : is the above operation safe with respect to memory visibility for Object oneTask ?
// is it possible that oneTask may appear null or in some other state than expected ?
}
}
Question : is the operation Object oneTask = localTasksArray[someindex];
safe with respect to memory visibility for Object oneTask ?
is it possible that oneTask may appear null or in some other state than expected ?
My thoughts are these :
It is possible that thread2 may see oneTask
as null or in some state other than expected. This is because , even though the taskArray
is volatile
, and a read of this array will ensure proper visibility of the array itself , this does not ensure the visibility of the state internal to the object oneTask
.
Upvotes: 4
Views: 739
Reputation: 65879
As far as I recall (I am researching now to confirm), Java only guarantees the volatile object itself is flushed to RAM, not sub parts of that object (as in array entries) or sub-fields of the object (in the case of objects).
However - I believe most (if not all) JVMs implement volatile
access as a memory barrier (see here and the referenced page The JSR-133 Cookbook for Compiler Writers). As a memory barrier this therefore means that all previous writes by other threads will be flushed from cache to main memory when the access occurs - thus making all memory consistent at that time.
This should not be relied upon - if it is crucial that you have complete control of what is visible and what is not you should use one of the Atomic
classes such as AtomicReferenceArray
as suggested by others. These may even be more efficient than volatile
for exactly this reason.
Upvotes: 4
Reputation: 70584
Declaring a field volatile ensures that any write of that variable synchronizes-with (and is therefore visible to) any subsequent (according to synchronization order) of this value. Note that there is no such thing as a volatile object, or a volatile array. There are only volatile fields.
Therefore, in your code, there is no happens-before relationship between Thread 1 storing an object into the array, and Thread 2 reading it from there.
Upvotes: 2
Reputation: 533870
The volatile
keyword only protects the field taskArray
which is a reference to an Object[]. Whenever you read or write this field, it will have consistent ordering. However, this doesn't extend to the array referenced nor the objects that array references.
Most likely you want AtomicReferenceArray instead.
Upvotes: 5