...
The Java Language Specification also permits reads and writes of 64-bit values to be non-atomic. (For more information, see CON25-J. Ensure atomicity when reading and writing 64-bit values.).
Noncompliant Code Example (
...
Logical Negation)
This noncompliant code example declares a shared boolean
flag
variable flag
and provides a toggle()
method that negates the current value of flag
.
...
Consider, for example, two threads that call toggle()
. The expected effect of toggling flag
twice is expected to restore it to its original value, however. However, the following scenario leaves flag
in the incorrect state:
...
As a result, the effect of the call by t2 is not reflected in flag
; the program behaves as if the call was never made.
Noncompliant Code Example (
...
Bitwise Negation)
Similarly, the toggle()
method can use the compound assignment operator ^=
to negate the current value of flag
.
...
This code is also not thread-safe. A data race exists because ^=
is a non-atomic compound operation.
Noncompliant Code Example (volatile
)
Declaring flag
as volatile also does not help either:
Code Block | ||
---|---|---|
| ||
final class Flag { private volatile boolean flag = true; public void toggle() { // Unsafe flag ^= true; } public boolean getFlag() { // Safe return flag; } } |
This code remains unsuitable for multithreaded use because declaring a variable as volatile does not guarantee the atomicity of compound operations on that variableit.
Compliant Solution (
...
Synchronization)
This compliant solution declares both the toggle()
and getFlag()
methods as synchronized
.
...
The second execution order involves the same operations, just that but t2 starts and finishes before t1.
Compliance with the guideline CON04-J. Synchronize using an internal private final lock object can reduce the likelihood of misuse by ensuring that untrusted callers cannot access the lock object.
Compliant Solution (
...
Volatile-
...
Read,
...
Synchronized-
...
Write)
In this compliant solution, the getFlag()
method is not synchronized, and flag
is declared as volatile. This solution is compliant because the read of flag
in the getFlag()
method is an atomic operation and the volatile qualification assures visibility. The toggle()
method still requires synchronization because it performs a non-atomic operation.
...
Wiki Markup |
---|
This approach may not be used when a getter method performs operations other than just returning the value of a {{volatile}} field without having to use any synchronization. Unless read performance is critical, this technique may not offer significant advantages over synchronization \[[Goetz 06|AA. Java References#Goetz 06]\]. |
The volatile-read, synchronized-write pattern is also addressed in CON11-J. Do not assume that declaring an object reference volatile guarantees visibility of its members also addresses the volatile-read, synchronized-write pattern.
Compliant Solution (
...
Read-
...
Write Lock)
This compliant solution uses a read-write lock to ensure atomicity and visibility.
...
Wiki Markup |
---|
Read-write locks allow a shared state to be accessed by multiple readers or a single writer, but never both. "In practice, read-write locks can improve performance for frequently accessed read-mostly data structures on multiprocessor systems; under other conditions they perform slightly worse than exclusive locks due to their greater complexity." \[[Goetz 06|AA. Java References#Goetz 06]\]. The Profiling the application can determine the suitability of read-write locks can be determined by profiling the application. |
Compliant Solution (AtomicBoolean
)
This compliant solution declares flag
as an AtomicBoolean
type.
Code Block | ||
---|---|---|
| ||
import java.util.concurrent.atomic.AtomicBoolean; final class Flag { private AtomicBoolean flag = new AtomicBoolean(true); public void toggle() { boolean temp; do { temp = flag.get(); } while(!flag.compareAndSet(temp, !temp)); } public AtomicBoolean getFlag() { return flag; } } |
The flag
variable is updated using the compareAndSet()
method of the AtomicBoolean
class AtomicBoolean
. All updates are visible to other threads.
Noncompliant Code Example (
...
Addition)
In this noncompliant code example, multiple threads may invoke the setValues()
method may be invoked by multiple threads to set the a
and b
fields. Because this class does not test for integer overflow, a user of the Adder
class must ensure that the arguments to the setValues()
method can be added without overflow. (For more information, see INT00-J. Perform explicit range checking to ensure integer operations do not overflow for more information.
Code Block | ||
---|---|---|
| ||
final class Adder { private int a; private int b; public int getSum() { return a + b; } public void setValues(int a, int b) { this.a = a; this.b = b; } } |
The getSum()
method contains a data race. For example, if a
and b
currently have the values 0 and Integer.MAX_VALUE
, respectively, and one thread calls getSum()
while another calls setValues(Integer.MAX_VALUE, 0)
, then getSum()
might return 0, Integer.MAX_VALUE, or it might overflow and wrap. Overflow will occur when the first thread reads a
and b
, after the second thread has set the value of a
to Integer.MAX_VALUE
but before it has set the value of b
to 0.
...