Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Given an invariant involving multiple objects, a programmer may might incorrectly assume that a group of individually atomic operations is collectively atomic without additional locking. Similarly, programmers may might incorrectly assume that use of a thread-safe Collection 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.

...

Compound operations on shared variables are also non-atomic. For more information, see See rule VNA02-J. Ensure that compound operations on shared variables are atomic for more information.

Rule VNA04-J. Ensure that calls to chained methods are atomic describes a specialized case of this rule.

...

Code Block
bgColor#FFCCCC
final class IPHolder {
  private final List<InetAddress> ips = 
      Collections.synchronizedList(new ArrayList<InetAddress>());

  public void addAndPrintIPAddresses(InetAddress address) {
    ips.add(address);
    InetAddress[] addressCopy = 
        (InetAddress[]) ips.toArray(new InetAddress[0]);
    // Iterate through array addressCopy ...
  }
}

Individually, the add() and toArray() collection methods are atomic. However, when called in succession (for example, in the addAndPrintIPAddresses() method), they lack any there is no guarantee that the combined operation is atomic. 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.

...

Code Block
bgColor#ccccff
final class IPHolder {
  private final List<InetAddress> ips = 
      Collections.synchronizedList(new ArrayList<InetAddress>());

  public void addAndPrintIPAddresses(InetAddress address) {
    synchronized (ips) {
      ips.add(address);
      InetAddress[] addressCopy = 
          (InetAddress[]) ips.toArray(new InetAddress[0]);
      // Iterate through array addressCopy ...
    }
  }
}

...

This code does not violate rule LCK04-J. Do not synchronize on a collection view if the backing collection is accessible because, while it does synchronize on a collection view (the synchronizedList result), the backing collection is inaccessible and consequently cannot be modified by any code.

...

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 tonot be atomic \[[Lee 2009|AA. Bibliography#Lee 09]\].

Code Block
bgColor#FFCCCC
final class KeyedCounter {
  private final Map<String, Integer> map =
      Collections.synchronizedMap(new HashMap<String, Integer>());

  public void increment(String key) {
    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) {
    return map.get(key);
  }
}

...

Code Block
bgColor#ccccff
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);
    }
  }
}

...

The previous compliant solution is safe for multithreaded use , but fails to does not scale well because of excessive synchronization, which can lead to contention and deadlock.

...

Code Block
bgColor#ccccff
final class KeyedCounter {
  private final ConcurrentMap<String, AtomicInteger> map =
      new ConcurrentHashMap<String, AtomicInteger>();

  public void increment(String key) {
    AtomicInteger value = new AtomicInteger();
    AtomicInteger old = map.putIfAbsent(key, value);

    if (old != null) {
      value = old;
    }

    if (value.get() == Integer.MAX_VALUE) {
      throw new ArithmeticException("Out of range");
    }

    value.incrementAndGet(); // Increment the value atomically
  }

  public Integer getCount(String key) {
    AtomicInteger value = map.get(key);
    return (value == null) ? null : value.get();
  }

  // Other accessors ...
}

...

Related Guidelines

MITRE CWE

CWE-362, ". Concurrent Execution execution using Shared Resource with Improper Synchronization ('Race Condition')" shared resource with improper synchronization ("race condition")

 

CWE-366, ". Race Condition condition within a Thread" thread

 

CWE-662, ". Improper Synchronization" synchronization

Bibliography

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="1c4fd592fadba82b-d51e66f4-4f37492d-b3478672-6ee53416a96cd48070305d94"><ac:plain-text-body><![CDATA[

[[API 2006

AA. Bibliography#API 06]]

 

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="706984ff69021500-4b019c96-4f9b4a38-89269b4e-dd82accee3fd7a3f26d72608"><ac:plain-text-body><![CDATA[

[[Goetz 2006

AA. Bibliography#Goetz 06]]

Section 4.4.1 ", Client-side Locking "

]]></ac:plain-text-body></ac:structured-macro>

 

Section 5.2.1, " ConcurrentHashMap "

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="2c1a3d62d462a92c-0fd10b12-434e46cf-a4c68339-73b9c41ebc0201e72e23e322"><ac:plain-text-body><![CDATA[

[[JavaThreads 2004

AA. Bibliography#JavaThreads 04]]

Section 8.2, " Synchronization and Collection Classes "

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="265dbd4f1f59ef71-a30a9854-48444d5b-b0e3995f-caf2cb031ae6cffc6029c708"><ac:plain-text-body><![CDATA[

[[Lee 2009

AA. Bibliography#Lee 09]]

" Map & Compound Operation "

]]></ac:plain-text-body></ac:structured-macro>

...