...
Wiki Markup |
---|
The use of {{volatile}} is recommended under a very restrictive set of conditions, all of which must be met \[[Goetz 06|AA. Java References#Goetz 06]\]: |
- A write to a variable does not depend on its current value, or it can be ensured that only a single thread ever updates the value
- The write is not involved with reads or writes of other variablesOnly a single thread ever updates the value
- Locking is not required for any other reason (all actions are atomic)
Synchronizing the code makes it easier to reason about the its behavior of the code and is frequently, a more secure approach than using volatile
. However, it is slightly more expensive and can cause deadlocks when used excessively.
Declaring a variable as volatile or correctly synchronizing the code guarantees that 64-bit primitive variables of type long
and double
are accessed atomically (see CON25-J. Ensure atomicity when reading and writing 64-bit values for information on sharing long
and double
variables among amongst multiple threads).
Noncompliant Code Example
...
Code Block | ||
---|---|---|
| ||
final class ControlledStop implements Runnable {
private boolean done = false;
@Override public void run() {
while (!done) {
try {
// ...
Thread.currentThread().sleep(1000); // Do something
} catch(InterruptedException ie) {
// handle exception
Thread.currentThread().interrupt(); // Reset interrupted status
}
}
}
public void shutdown() {
done = true;
}
}
|
If one thread invokes the shutdown()
method to set the flag, it is possible that another thread might not observe this change. Consequently, the second thread may observe that done
is still false
and incorrectly invoke the sleep()
method. In fact, a compiler is allowed to optimize the code if it determines that the value of done
is never modified by the same thread, so that the loop never terminateswith the end result being an infinite loop.
Compliant Solution (volatile
)
...
Code Block | ||
---|---|---|
| ||
final class ControlledStop implements Runnable {
private final AtomicBoolean done = new AtomicBoolean(false);
public void run() {
while (!done.get()) {
try {
// ...
Thread.currentThread().sleep(1000); // Do something
} catch(InterruptedException ie) {
// handle exception
Thread.currentThread().interrupt(); // Reset interrupted status
}
}
}
public void shutdown() {
done.set(true);
}
}
|
...
While this is an acceptable compliant solution, it has the following shortcomings as compared to the previously suggested ones:
- Performance: The intrinsic Intrinsic locks cause threads to block temporarily and may introduce some contention;
volatile
incurs no blocking - Deadlock: Excessive synchronization can make the program deadlock prone.
However, synchronization is a more secure alternative in situations where the volatile
keyword or an a java.util.concurrent.atomic.Atomic*
field is inappropriate, such as if a variable's new value depends on its old value. Refer to CON01-J. Ensure that compound operations on shared variables are atomic for more information.
...