Wiki Markup |
---|
Compound operations are operations that consist of more than one discrete operation. Expressions that include postfix and prefix increment ({{\+\+}}), postfix or prefix decrement ({{\-\-}}), or compound assignment operators always result in compound operations. Compound assignment expressions includeuse operators such as {{*=, /=, %=, +=, -=, <<=, >>=, >>>=, ^=}} and {{|=}} \[[JLS 05|AA. Java References#JLS 05]\]. Compound operations on shared variables must be performed atomically to prevent [data races|BB. Definitions#data race] and [race conditions|BB. Definitions#race conditions]. |
...
Execution of this code may result in a data race because the value of flag
is read, negated, and written back.
...
Consider, for example, two threads that call toggle()
. The 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:
...
This code remains unsuitable for multithreaded use because declaring a variable as volatile does not guarantee that the atomicity of compound operations on that variable are performed atomically.
Compliant Solution (synchronization)
...
Wiki Markup |
---|
This approach may not be used when a getter method performs operations other than just returning the value of a {{volatile}} field that require, 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.
Compliant Solution (read-write lock)
This compliant solution uses a read-write lock to ensure atomicity and visibility.
Code Block | ||
---|---|---|
| ||
final class Flag {
private boolean flag = true;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
public synchronized void toggle() {
writeLock.lock();
try {
flag ^= true; // Same as flag = !flag;
} finally {
writeLock.unlock();
}
}
public boolean getFlag() {
readLock.lock();
try {
return flag;
} finally {
readLock.unlock();
}
}
}
|
Wiki Markup |
---|
Read-write locks allow 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 suitability of read-write locks can be determined by profiling the application. |
Compliant Solution (java.util.concurrent.atomic.AtomicBoolean
)
...