...
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 |
. According to Goetz \[[Goetz 06|AA. Java References#Goetz 06]\]: |
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 .
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]\]. Wiki Markup
Profiling the application can determine the suitability of read-write locks.
...
The flag
variable is updated using the compareAndSet()
method of the AtomicBoolean
class. All updates are visible to other threads.
Noncompliant Code Example (Addition of primitives)
In this noncompliant code example, multiple threads can invoke the setValues()
method 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.)
...
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)
, getSum()
might return 0 or 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.
Note that declaring the variables as volatile does not resolve the issue because these compound operations involve reads and writes of multiple variables.
Noncompliant Code Example (Addition of atomic integers)
In this noncompliant code example, a
and b
are replaced with atomic integers.
Code Block | ||
---|---|---|
| ||
final class Adder {
private final AtomicInteger a = new AtomicInteger();
private final AtomicInteger b = new AtomicInteger();
public int getSum() {
return a + b;
}
public void setValues(int a, int b) {
this.a = a;
this.b = b;
}
}
|
This does not eliminate the data race because compound operation a + b
is still non-atomic.
Compliant Solution (Addition)
This compliant solution synchronizes the setValues()
and getSum()
methods to ensure atomicity.
Code Block | ||
---|---|---|
| ||
final class Adder {
private int a;
private int b;
public synchronized int getSum() {
return a + b;
}
public synchronized void setValues(int a, int b) {
this.a = a;
this.b = b;
}
}
|
Noncompliant Code Example (Overflow Check, Atomic Integer Fields)
...