For your particular example: if not declared volatile the server JVM could hoist the keepRunning
variable out of the loop because it is not modified in the loop (turning it into an infinite loop), but the client JVM would not. That is why you see different results.
General explanation about volatile variables follows:
When a field is declared volatile
, the compiler and runtime are put on notice that this variable is shared and that operations on it should not be reordered with other memory operations. Volatile variables are not cached in registers or in caches where they are hidden from other processors, so a read of a volatile variable always returns the most recent write by any thread.
The visibility effects of volatile variables extend beyond the value of the volatile variable itself. When thread A writes to a volatile variable and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the volatile variable become visible to B after reading the volatile variable.
The most common use for volatile variables is as a completion, interruption, or status flag:
volatile boolean flag;
while (!flag) {
// do something untill flag is true
}
Volatile variables can be used for other kinds of state information, but more care is required when attempting this. For example, the semantics of volatile are not strong enough to make the increment operation (count++
) atomic, unless you can guarantee that the variable is written only from a single thread.
Locking can guarantee both visibility and atomicity; volatile variables can only guarantee visibility.
You can use volatile variables only when all the following criteria are met:
- Writes to the variable do not depend on its current value, or you can
ensure that only a single thread ever updates the value;
- The variable does not participate in invariants with other state variables; and
- Locking is not required for any other reason while the variable is being accessed.
Debugging tip: be sure to always specify the -server
JVM command line switch when invoking the JVM, even for development and testing. The server JVM performs more optimization than the client JVM, such as hoisting variables out of a loop that are not modified in the loop; code that might appear to work in the development environment (client JVM) can break in the deployment environment
(server JVM).
This is an excerpt from "Java Concurrency in Practice", the best book you can find on this subject.
volatile
guarantees that the change to the field will be visible. Without the keyword, simply there are no guarantees at all, anything can happen; you cannot state that thread should keep on running [...]. – MceachernSets true: value=true target=true
andSets false: value=false target=false
, BUT: tons of circumstances can lead to same output... Tried to put same on blogspot, but "Comments are restricted to team members" – Albrecht