Considering the following toy example class:
public class Test {
private volatile Outer outerVar = new Outer();
static class Outer {
Inner innerVar = new Inner();
}
static class Inner {
// state
// setters
// getters
}
private void multithreadedUse() {
// play with outerVar.innerVar
}
}
outerVar is volatile so all threads that may be using it will see it in the same state. But what about outerVar.innerVar? Does the fact that its parent object ( outerVar ) is marked as volatile make it volatile also?
Or do we have to declare innerVar volatile explicitly?
If you write volatile variable from multiple threads without using any synchronized constructs, you are bound to get data inconsistency errors. Use volatile variables without synchronization in case of single write thread and multiple read threads for atomic operations.
Volatile keyword is used to modify the value of a variable by different threads. It is also used to make classes thread safe. It means that multiple threads can use a method and instance of the classes at the same time without any problem. The volatile keyword can be used either with primitive type or objects.
Unlike synchronized methods or blocks, it does not make other threads wait while one thread is working on a critical section. Therefore, the volatile keyword does not provide thread safety when non-atomic operations or composite operations are performed on shared variables.
Volatile variables have the visibility features of synchronized but not the atomicity features. The values of the volatile variable will never be cached and all writes and reads will be done to and from the main memory.
But what about outerVar.innerVar? Does the fact that its parent object ( outerVar ) is marked as volatile make it volatile also?
In this example outerVar.innerVar
will be published correctly, but it will not be volatile. If you were to assign outerVar.innerVar = new Inner()
as some later point you will lose the thread-safe publication.
The rule here is that all write that occur prior to a volatile write are visible after the volatile write. After that write, all normal writes are now thread-unsafe.
So in your example the ordering from a thread would see something similar to
volatile Outer outerVar;
Outer temp = new Outer();
temp.innerVal = new Inner()
outerVar = temp;
Notice the volatile write of outVar = temp
. This is where the synchronization starts. And when another thread reads a non-null outerVar instance, the innerVar field will be safely published.
To reiterate though, any time you assign a new value to outerVar.innerVal
you will lose synchornization. Similarly if innerVal
has any field [after the initial volatile write] writes to those fields will not be correctly synchronized
So to answer your question
Is Java's volatile keyword “recursive” regarding references tree, or must each reference be declared as volatile?
Each field must be declared volatile that will change after the initial volatile write (technically speaking). That being said, you should declare fields either volatile or final if you are sharing among threads.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With