A consistent locking policy guarantees that multiple threads cannot simultaneously access or modify shared data. If When two or more operations need to must be performed as a single atomic operation, a consistent locking policy must be implemented using either intrinsic synchronization or java.util.concurrent
utilities. In the absence of such a policy, the code is susceptible to race conditions.
Given an invariant involving multiple objects, a programmer may incorrectly assume that a group of individually atomic operations require no is collectively atomic without additional locking. Similarly, programmers may incorrectly assume that using use of a thread-safe Collection
does not require explicit synchronization is sufficient to preserve an invariant that involves the collection's elements without additional synchronization. A thread-safe class can only guarantee atomicity of its individual methods. A grouping of calls to such methods requires additional synchronization.
Consider, for example, a scenario where the standard thread-safe API does not provide lacks a single method to both find a particular person's record in a Hashtable
and also update the corresponding payroll information. In such cases, the two method invocations must be performed atomically.
Enumerations and iterators also require either explicit synchronization on the collection object (client-side locking) or use of a private final lock object.
...
This noncompliant code example wraps references to BigInteger
objects within thread-safe AtomicReference
objects.
...
This noncompliant code example uses a java.util.ArrayList<E>
collection, which is not thread-safe. However, the example uses Collections.synchronizedList
is used as a synchronization wrapper for the ArrayList
. An It subsequently uses an array, rather than an iterator, is used to iterate over the ArrayList
to avoid a ConcurrentModificationException
.
...
Individually, the add()
and toArray()
collection methods are atomic. However, when they are called in succession (for example in the addAndPrintIPAddresses()
method), there are no guarantees they lack any guarantee that the combined operation is atomic. A race condition exists in the The addAndPrintIPAddresses()
method contains a race condition that allows one thread to add to the list and a second thread to race in and modify the list before the first thread completes. Consequently, the addressCopy
array may contain more IP addresses than expected.
...
Wiki Markup |
---|
This noncompliant code example defines the {{KeyedCounter}} class that is not thread-safe. Although the {{HashMap}} is wrapped in a {{synchronizedMap}}, the overall increment operation is fails to notbe atomic \[[Lee 2009|AA. Bibliography#Lee 09]\]. |
...
Compliant Solution (Synchronization)
To ensure atomicity, this This compliant solution uses ensures atomicity by using an internal private lock object to synchronize the statements of the increment()
and getCount()
methods.
Code Block | ||
---|---|---|
| ||
final class KeyedCounter { private final Map<String, Integer> map = new HashMap<String, Integer>(); private final Object lock = new Object(); public void increment(String key) { synchronized (lock) { Integer old = map.get(key); int oldValue = (old == null) ? 0 : old.intValue(); if (oldValue == Integer.MAX_VALUE) { throw new ArithmeticException("Out of range"); } map.put(key, oldValue + 1); } } public Integer getCount(String key) { synchronized (lock) { return map.get(key); } } } |
This compliant solution does not use avoids using Collections.synchronizedMap()
because locking on the unsynchronized map provides sufficient thread-safety for this application. Guideline LCK04-J. Do not synchronize on a collection view if the backing collection is accessible provides more information about synchronizing on synchronizedMap
objects.
...
The previous compliant solution is safe for multithreaded use, but it does not fails to scale well because of excessive synchronization, which can lead to contention and deadlock.
...
Note that methods such as ConcurrentHashMap.size()
and ConcurrentHashMap.isEmpty()
are allowed to return an approximate result for performance reasons. Code should not rely avoid relying on these return values for deriving when exact results are required.
Risk Assessment
Failing Failure to ensure the atomicity of two or more operations that need to must be performed as a single atomic operation can result in race conditions in multithreaded applications.
Guideline | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
VNA03-J | low | probable | medium | P4 | L3 |
...
TODO
Related Vulnerabilities
Any vulnerabilities resulting from the violation of this guideline are listed on the CERT website.
...
Wiki Markup |
---|
\[[API 2006|AA. Bibliography#API 06]\] \[[JavaThreadsGoetz 20042006|AA. Bibliography#JavaThreadsBibliography#Goetz 0406]\] Section 84.4.21, "Synchronization and Collection ClassesClient-side Locking," Section 5.2.1, "ConcurrentHashMap" \[[GoetzJavaThreads 20062004|AA. Bibliography#GoetzBibliography#JavaThreads 0604]\] Section 48.4.12, "Client-side Locking," Section 5.2.1, "ConcurrentHashMapSynchronization and Collection Classes" \[[Lee 2009|AA. Bibliography#Lee 09]\] "Map & Compound Operation" |
...