Java provides a second mechanism that is more convenient for some purposes: a field may be declared volatile
, in which case a thread must reconcile its working copy of the field with the master copy every time it accesses the variable. Moreover, operations on the master copies of one or more volatile variables on behalf of a thread are performed by the main memory in exactly the order that the thread requested.
If, in the following example, one thread repeatedly calls the method one
(but no more than Integer.MAX_VALUE
(§20.7.2) times in all), and another thread repeatedly calls the method two
:
class Test {then method
static int i = 0, j = 0;
static void one() { i++; j++; } static void two() { System.out.println("i=" + i + " j=" + j); }
}
two
could occasionally print a value for j
that is greater than the
value of i
, because the example includes no synchronization and, under the rules
explained in §17, 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
(§8.4.3.5):
class Test {This prevents method
static int i = 0, j = 0;
static synchronized void one() { i++; j++; } static synchronized void two() { System.out.println("i=" + i + " j=" + j); }
}
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
:
class Test {This allows method
static volatile int i = 0, j = 0;
static void one() { i++; j++; } static void two() { System.out.println("i=" + i + " j=" + j); }
}
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, method two
never observes a value for j
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
.
See §17 for more discussion and examples.
A compile-time error occurs if a final
variable is also declared volatile
.
volatile
Fieldsprivate
was not declared volatile
and is changed
to be declared volatile
, or vice versa, then a linkage time error, specifically an
IncompatibleClassChangeError
, may result if the field is used by a preexisting
binary that expected a field of the opposite volatility. Such changes are not recommended in code that has been widely distributed.