For your specific example: if volatile is not declared, the server-side JVM can take the keepRunning variable keepRunning of the loop because it is not changed in the loop (turning it into an infinite loop), but the client JVM will not. That is why you see different results.
The following is a general explanation of volatile variables:
When a field is declared volatile , the compiler and runtime are notified that this variable is shared and that its operations 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 reading a volatile variable always returns the most recent record by any thread .
The effects of the visibility of variable variables go beyond the value of the variable itself. When stream A writes to volatile and subsequently stream B reads the same variable, the values ββof all the variables that were visible to A before writing to volatile become visible to B after reading volatile.
The most commonly used variables change as a completion, interrupt, or state flag:
volatile boolean flag; while (!flag) { // do something untill flag is true }
Volatile variables can be used for other types of state information, but more attention is required when trying to do 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 from only one stream.
Blocking can guarantee both visibility and atomicity; mutable variables can only guarantee visibility.
You can use variable variables only if all of the following criteria are met:
- Writing to a variable does not depend on its current value, or you can make sure that only one thread updates the value;
- The variable does not participate in invariants with other state variables; and
- Locking is not required for any other reason when accessing a variable.
Debugging tip : -server sure to specify the JVM -server command line -server when invoking the JVM, even for development and testing. The server-side JVM performs more optimization than the client JVM, for example, the output of variables from a loop that have not been changed in the loop; code that can run in a development environment (client JVM) may break in a deployment environment (JVM server).
This is an excerpt from "Java Concurrency in Practice," the best book on the subject.