...
- The lock object might be accessible to hostile code that can acquire the lock and hold it indefinitely. We recommend that locks not be accessible outside of their containing packageLocks should be made inaccessible outside the package they are declared in.
- A non-final field referring to a lock object might can be modified to refer to a different lock object. This can cause two critical sections of code that expect to lock on the same object to lock on different objects instead, or, two critical sections intending to lock on different objects may end up locking to lock on the same shared object. This can result in unanticipated outcomes.
- Any An object that supports the uses dynamic locking, such as through
java.util.concurrent.Lock
interfaceReentrantLock
, should not have expose its intrinsic lock used; as so that other code can use it because this may cause confusion and inconsistency during maintainanceintroduce maintenance issues.
Noncompliant Code Example (public
nonfinal lock object)
...
This is because the thread that holds a lock on the nonfinal field object can modify the field's value to reference refer to some other object. This might cause two threads that intend to lock on the same field object to actually not lock on the same object, causing different objects, enabling them to execute the two critical sections of code simultaneouslyin an unsafe manner.
Compliant Solution (private
and final
lock object)
This compliant solution synchronizes using a lock object that is declared as private
and final
.
Code Block | ||
---|---|---|
| ||
private final Integer lock = new Integer(0); private void doSomething() { synchronized(lock) { /* ... */ } } // setValue() is disallowed |
Noncompliant Code Example (Boolean
lock object)
This noncompliant code example uses a {{ Wiki Markup Boolean
}} field for synchronization. However, because the field is non-final, there can be two possible valid values ({{true}} and {{false}}, discounting {{null}}) that a {{Boolean}} can assume. Consequently, any other code that synchronizes on the same value can cause unresponsiveness and deadlocks \[[Findbugs 08|AA. Java References#Findbugs 08]\].
Code Block | ||
---|---|---|
| ||
private Boolean initialized = Boolean.FALSE;
synchronized(initialized) {
if (!initialized) {
// Perform initialization
initialized = Boolean.TRUE;
}
}
|
Code Block | ||
---|---|---|
| ||
private Boolean initialized = Boolean.FALSE;
synchronized(initialized) {
if (!initialized) {
// Perform initialization
initialized = Boolean.TRUE;
}
}
|
Wiki Markup |
---|
However, because the field is non-final, there can be two possible valid values ({{true}} and {{false}}, discounting {{null}}) that a {{Boolean}} can assume. Consequently, any other code that synchronizes on the same value can cause unresponsiveness and deadlocks \[[Findbugs 08|AA. Java References#Findbugs 08]\]. Even if the field were final, the code would use the intrinsic lock of {{Boolean.FALSE}} or {{Boolean.TRUE}}, which are accessible throughout the program. |
Boolean.FALSE
or Boolean.TRUE
, which are accessible throughout the program. Consequently any other code could lock these objects and cause deadlock.Noncompliant Code Example (Boxed primitive)
...
Consequently, a String
constant behaves like a global variable in the JVM. As demonstrated in this noncompliant code example, even if each every instance of an object maintains its own field lock
, the field points to a common String
constant in the JVM. Trusted code that locks on the same String
constant renders all synchronization attempts inadequate. Likewise, hostile code from any other package can exploit this vulnerability.
...
This does not mean that a subclass using getClass()
can only synchronize on the Class
object of the base class. In fact, it will lock on its own Class
object, which may or may not be want the programmer had in mind. The intent should be appropriately documented or annotated.
Compliant Solution (class name qualification)
...
This noncompliant code example uses a nonstatic lock object to guard access to a static
field. If two Runnable
tasks, each consisting of a thread are started, they will create two instances of the lock object and lock on each separately. This does not prevent either thread from observing an inconsistent value of counter
because the increment operation on volatile
fields is not atomic in the absence of proper synchronizationof the lock object and lock on each separately.
Code Block | ||
---|---|---|
| ||
class CountBoxes implements Runnable { static volatile int counter; // ... Object lock = new Object(); public void run() { synchronized(lock) { counter++; // ... } } public static void main(String[] args) { Runnable r1 = new CountBoxes(); Thread t1 = new Thread(r1); Runnable r2 = new CountBoxes(); Thread t2 = new Thread(r2); t1.start(); t2.start(); } } .start(); t2.start(); } } |
This does not prevent either thread from observing an inconsistent value of counter
because the increment operation on volatile
fields is not atomic in the absence of proper synchronization.
Noncompliant Code Example (method synchronization for static
data)
...
This problem usually comes up in practice when refactoring from intrinsic locking to the java.util.concurrent
dynamic locking utilities.
Compliant Solution (lock()
and unlock()
)
...
Noncompliant Code Example (collection view)view)
Finally, it is more important to recognize the entities with whom synchronization is required rather than indiscreetly scavenging for variables or objects to synchronize on. This noncompliant code example synchronizes on the view of a synchronized map.
Code Block | ||
---|---|---|
| ||
Map<Integer, String> mmap = Collections.synchronizedMap(new HashMap<Integer, String>()); Set<Integer> sset = mmap.keySet(); synchronized(sset) { // Incorrectly synchronizes on sset for(Integer k : sset) { // Do something } } |
Wiki Markup |
---|
When using synchronization wrappers, the synchronization object must be the {{Collection}} object. The synchronization is necessary to enforce atomicity ([CON07-J. Do not assume that a grouping of calls to independently atomic methods is atomic]). This noncompliant code example demonstrates inappropriate synchronization resulting from locking on a Collection view instead of the Collection object itself \[[Tutorials 08|AA. Java References#Tutorials 08]\]. |
Wiki Markup |
---|
The Collections class documentation \[[API 06|AA. Java References#API 06]\] saysstates: |
It is imperative that the user manually synchronize on the returned map when iterating over any of its collection views... Failure to follow this advice may result in non-deterministic behavior.
...
Code Block | ||
---|---|---|
| ||
// ... Map<Integer, String> mmap = Collections.synchronizedMap(new HashMap<Integer, String>()); synchronized(mmap) { // Synchronize on mmap, not sset for(Integer k : mmap) { // Do something } } |
Finally, it is more important to recognize the entities with whom synchronization is required rather than indiscreetly scavenging for variables or objects to synchronize on.
Risk Assessment
Synchronizing on an incorrect variable can provide a false sense of thread safety and result in nondeterministic behavior.
...