I'm reading about the Java volatile
keyword and have confusion about its 'visibility'.
A typical usage of volatile keyword is:
volatile boolean ready = false;
int value = 0;
void publisher() {
value = 5;
ready = true;
}
void subscriber() {
while (!ready) {}
System.out.println(value);
}
As explained by most tutorials, using volatile for ready
makes sure that:
- change to
ready
on publisher thread is immediately visible to other threads (subscriber); - when
ready
's change is visible to other thread, any variable update preceding toready
(here isvalue
's change) is also visible to other threads;
I understand the 2nd, because volatile
variable prevents memory reordering by using memory barriers, so writes before volatile write cannot be reordered after it, and reads after volatile read cannot be reordered before it. This is how ready
prevents printing value
= 0 in the above demo.
But I have confusion about the 1st guarantee, which is visibility of the volatile variable itself. That sounds a very vague definition to me.
In other words, my confusion is just on SINGLE variable's visibility, not multiple variables' reordering or something. Let's simplify the above example:
volatile boolean ready = false;
void publisher() {
ready = true;
}
void subscriber() {
while (!ready) {}
}
If ready
is not defined volatile, is it possible that subscriber get stuck infinitely in the while loop? Why?
A few questions I want to ask:
- What does 'immediately visible' mean? Write operation takes some time, so after how long can other threads see volatile's change? Can a read in another thread that happens very shortly after the write starts but before the write finishes see the change?
- Visibility, for modern CPUs is guaranteed by cache coherence protocol (e.g. MESI) anyway, then why do we need
volatile
here? - Some articles say volatile variable uses memory directly instead of CPU cache, which guarantees visibility between threads. That doesn't sound a correct explain.
Time : ---------------------------------------------------------->
writer : --------- | write | -----------------------
reader1 : ------------- | read | -------------------- can I see the change?
reader2 : --------------------| read | -------------- can I see the change?
Hope I explained my question clearly.
reader1
in your example can do what it does. You either read the value before the write, or after the write. – Simplyreader1
cannot see the change. But I'm still trying to figure outreader2
's view. Canreader2
definitely see the change? What if variable is not volatile, can reader2 still see what is wants? What difference does volatile actually makes here? – Carranzareader1
cannot see the change. I saidreader1
can't be reading after the write starts but before the write ends, because writes are atomic. Reads either occur before, or after the write. If the variable is not volatile,reader2
might not see the change. – Simplyreader2
see the change after sufficiently long time? Or do you meanreader2
may never see it? As I understand, CPU cache coherence guaranteesreader2
will finally see the change, not after very long time. So what is the difference between using volatile or not? – Carranza