...
The Java 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
...
In this noncompliant code example, the volatile
field itemsInInventory
can be accessed by multiple threads. However, when a thread is updating the value of itemsInInventory
, it is possible for other threads to read the original value (that is, the value before the update). This is because the post decrement operator is non-atomic.
Code Block | ||
---|---|---|
| ||
private int itemsInInventory = 100; public int removeItem() { if(itemsInInventory > 0) { return itemsInInventory--; // Returns new count of items in inventory } return -1; // Error code } |
Noncompliant Code Example (volatile
)
This noncompliant code example attempts to fix the race condition by declaring itemsInInventory
as volatile
. This guarantees that updates to the field are immediately visible to any thread that reads the field. However, when a thread is updating the value of itemsInInventory
, it is still possible for other threads to read the original value (that is, the value before the update). This is because the post decrement operator is non-atomic.
Code Block | ||
---|---|---|
| ||
private volatile int itemsInInventory = 100;
public int removeItem() {
if(itemsInInventory > 0) {
return itemsInInventory--; // Returns new count of items in inventory
}
return -1; // Error code
}
|
...
Code that uses this lock behaves similar to synchronized code that uses the traditional monitor lock. In addition, it provides several other capabilities, for instance, the tryLock()
method does not block waiting if another thread is already holding the lock. The class java.util.concurrent.locks.ReentrantReadWriteLock
can be used when a thread requires a lock to write information while other threads require the lock to simultaneously read the information.
Noncompliant Code Example
In this noncompliant code example, the two fields a
and b
may be set by multiple threads, using the setValues
method. While getSum()
is always guaranteed to return a sum, that sum might be erroneous.
Code Block | ||
---|---|---|
| ||
private volatile int a, b;
public int getSum() {
return a + b;
}
public int setValues(int a, int b) {
this.a = a;
this.b = b;
}
|
For instance, if a
and b
currently have the value 0, and thread 1 calls getSum()
while thread 2 calls setValues( 1, 1)
, then getSum() might return 0, 2, or 1. The latter case occurs if thread 1 reads a
before thread 2 modifies it, and then thread 1 reads b
after thread 2 has modified it.
Compliant Solution
This compliant solution provides atomicity by method synchronization.
Code Block | ||
---|---|---|
| ||
private volatile int a, b;
public synchronized int getSum() {
return a + b;
}
public synchronized int setValues(int a, int b) {
this.a = a;
this.b = b;
}
|
As in the previous example, if a
and b
currently have the value 0, and thread 1 calls getSum()
while thread 2 calls setValues( 1, 1)
, then getSum()
may return return 0, or 2, depending on which thread grabs the intrinsic lock first. But the locking guarantees that getSum()
will not return 1.
Risk Assessment
If operations on shared variables are not atomic, unexpected results may be produced. For example, there can be inadvertent information disclosure as one user may be able to receive information about other users.
...