Kevan, the code you have written doesn't demonstrate use of volatile.
I am just pasting the explanation over volatile written in Java language specification.
The Java programming language allows threads to access shared variables. As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.
The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes.
A field may be declared volatile, in which case the Java memory model ensures that all threads see a consistent value for the variable. If, in the following example, one thread repeatedly calls the method one (but no more than Integer.MAX_VALUE times in all), and another thread repeatedly calls the method two:
then method two could occasionally print a value for j that is greater than the value of i, because the example includes no synchronization and the shared values of i and j might be updated out of order. One way to prevent this out-or-order behavior would be to declare methods one and two to be synchronized:
This prevents method one and method two from being executed concurrently, and furthermore guarantees that the shared values of i and j are both updated before method one returns. Therefore method two never observes a value for j greater than that for i; indeed, it always observes the same value for i and j.
Another approach would be to declare i and j to be volatile:
This allows method one and method two to be executed concurrently, but guarantees that accesses to the shared values for i and j occur exactly as many times, and in exactly the same order, as they appear to occur during execution of the program text by each thread. Therefore, the shared value for j is never greater than that for i, because each update to i must be reflected in the shared value for i before the update to j occurs. It is possible, however, that any given invocation of method two might observe a value for j that is much greater than the value observed for i, because method one might be executed many times between the moment when method two fetches the value of i and the moment when method
two fetches the value of j.
A compile-time error occurs if a final variable is also declared volatile.