Yes, this code is 'correct' as it stands, from Java 1.5 onwards.
Atomicity is not a concern, with or without the volatile (writes to object references are atomic), so you can cross that off the concerns list either way -- the only open question is visibility of changes and the 'correctness' of the ordering.
Any write to a volatile variable sets up a 'happens-before' relationship (the key concept of the new Java Memory Model, as specified in JSR-133) with any subsequent reads of the same variable. This means that the reading thread must have visibility into everything visible to the writing thread: that is, it must see all variables with at least their 'current' values at the time of the write.
We can explain this in detail by looking at section 17.4.5 of the Java Language Specification, specifically the following key points:
- "If x and y are actions of the same thread and x comes before y in program order, then hb(x, y)" (that is, actions on the same thread cannot be reordered in a way to be inconsistent with program order)
- "A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field." (this is clarifying text, explaining that write-then-read of a volatile field is a synchronization point)
- "If hb(x, y) and hb(y, z), then hb(x, z)" (transitivity of happens-before)
So in your example:
- the write to 'service' (a) happens-before the write to 'serviceReady' (b), due to rule 1
- the write to 'serviceReady' (b) happens-before the read of same (c), due to rule 2
- therefore, (a) happens-before (c) (3rd rule)
meaning that you are guaranteed that 'service' is set correctly, in this instance, once serviceReady is true.
You can see some good write-ups using almost exactly the same example, one at IBM DeveloperWorks -- see "New Guarantees for Volatile":
values that were visible to A at the time that V was written are guaranteed now to be visible to B.
and one at the JSR-133 FAQ, written by the authors of that JSR:
Thus, if the reader sees the value true for v, it is also guaranteed to see the write to 42 that happened before it. This would not have been true under the old memory model. If v were not volatile, then the compiler could reorder the writes in writer, and reader's read of x might see 0.