Java volatile and side-effects
Asked Answered
U

2

6

Oracle's documentation on atomic access (at http://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html) says this:

"a volatile variable establishes a happens-before relationship... . This means that ... when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change."

I'm having trouble wrapping my head around that. I understand how volatile variables work (in >= Java 5), but I'm wondering how the java decides what side-affect "led up" to the change of a volatile variable.

So I guess my question is: What side-effects are given this guarantee?

EDIT:

So I've learned that if thread A modifies a volatile variable, and then thread B reads it, all writes from thread A that happened before the write to the volatile variable are "made coherent" with respect to thread B (ie the cached values of variables subject to the afore mentioned writes by thread A are invalidated in thread B). Correct me if I'm wrong.

Upturned answered 7/2, 2012 at 0:10 Comment(6)
you do have misconception how the cache works BUT volatile write DOES FLUSH the write buffers and not the cache (cache can be flushed manually but generally you never want that)Milium
Whats my misconception exactly? Sounds like your answer would differ from Tom Hawtin's - maybe you want to write an alternative answer?Upturned
no, it doesn't contradict his answer, although it (his answer) is not exactly complete. The cache is not flushed (that would just destroy any performance) but only the modified cache lines are made coherent [to put it simply]. Also it ensures no speculative reads past write (this is super important). So when the CPU resumes all others would/could know about that write (and all previous writes). Generally speaking, all CPUs make write available/visible at some point but w/ volatile write it's just now. I dont think SO is a good area to explain how cache coherency works.Milium
btw, by "flush" do you mean "evict"? Flushing the cache usually means to evict its entries, not just to propagate the write.Milium
I was unclear in what I meant. Just ignore that statement. Question: what do you mean by "past write" ?Upturned
@BT Explaining cache coherency in SO comments won't work I fear (but I'll try). Basically it means that the CPU speculatively loads stuff from memory before it actually "should". Say we have two variables a and b b being volatile. Our code executes if (b) read a. In "asm": Instead of ld r0, b; jnz r0, ...; ld r0, a the processor could do ld r1, a; ld r0, b; jnz r0, ...; And now we have an obvious race condition: We could read an old value of a and the new value of b and never bother checking whether a changed.Bielefeld
E
5

Most multiprocessor caches have coherency mechanisms, so the penalty isn't as bad as flushing all the caches.

Any writes in the thread that wrote to the volatile before doing so will be seen by the thread reading the volatile after having done so.

Ethicize answered 7/2, 2012 at 0:24 Comment(4)
Ah this makes a lot of sense. So everything in the cache for that particular thread will be flushed? Java doesn't have a way to indicate that a variable will definitely not be accessed by another thread (and so would not need to be flushed)? Or perhaps the compiler/interpreter performs some kind of optimizations around that?Upturned
Also, I like your wording "after having done so" - so reads from thread A to non-volatiles that have changed in thread B may be out of date until thread A reads a volatile changed by thread B, correct?Upturned
@BT I'm no expert on cache coherency. It's necessary that no other cache is "thinks" it has a clean copy of the cache line. There's certainly no need to invalidate everything. Local variables can remain in registers. There's some opportunity to keep heap allocated objects, but it's pretty negligible. / Yes.Ethicize
@BT It completely depends on the cache coherency protocol that is used. In general we just need to propagate the write of all modified cache lines (if the protocol offers that information - no idea about ARM but the more complex desktop CPUs certainly do) not the whole cache.Bielefeld
S
3

Take Double Checked Locking as an example. When you create an object, many things happen under the covers:

MyClass c=new MyClass();

Memory is allocated, the constructor is called, and the memory location is assigned to the variable c. The JVM is allowed to reorder those actions. This causes problems if memory is allocated, the value is assigned, and a thread interrupts and uses the value before the constructor is called.

volatile MyClass c=new MyClass();

Under the 1.5 rules, the assignment is guaranteed to be the last one of those events. The "side effects" are the allocation and the constructor call.

Spectra answered 7/2, 2012 at 0:28 Comment(1)
Thats a reasonable example, but it doesn't explain how java decides what side affects to propogate or when. Tom's answer explains this very concisely.Upturned

© 2022 - 2024 — McMap. All rights reserved.