Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Added ThreadSafe detection

...

This noncompliant code example wraps references to BigInteger objects within thread-safe AtomicReference objects.

Code Block
bgColor#FFcccc

final class Adder {
  private final AtomicReference<BigInteger> first;
  private final AtomicReference<BigInteger> second;

  public Adder(BigInteger f, BigInteger s) {
    first  = new AtomicReference<BigInteger>(f);
    second = new AtomicReference<BigInteger>(s);
  }

  public void update(BigInteger f, BigInteger s) { // Unsafe
    first.set(f);
    second.set(s);
  }

  public BigInteger add() { // Unsafe
    return first.get().add(second.get());
  }
}

...

This compliant solution declares the update() and add() methods synchronized to guarantee atomicity.

Code Block
bgColor#ccccff

final class Adder {
  // ...
  private final AtomicReference<BigInteger> first;
  private final AtomicReference<BigInteger> second;

  public Adder(BigInteger f, BigInteger s) {
    first  = new AtomicReference<BigInteger>(f);
    second = new AtomicReference<BigInteger>(s);
  }



  public synchronized void update(BigInteger f, BigInteger s){
    first.set(f);
    second.set(s);
  }

  public synchronized BigInteger add() {
    return first.get().add(second.get());
  }
}

...

This noncompliant code example uses a java.util.ArrayList<E> collection, which is not thread-safe. However, the example uses Collections.synchronizedList as a synchronization wrapper for the ArrayList. It subsequently uses an array, rather than an iterator, to iterate over the ArrayList to avoid a ConcurrentModificationException.

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 ...
  }
}

...

The race condition can be eliminated by synchronizing on the underlying list's lock. This compliant solution encapsulates all references to the array list within synchronized blocks.

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 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 not atomic [Lee 2009].

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);
  }
}

...

This compliant solution ensures atomicity by using an internal private lock object to synchronize the statements of the increment() and getCount() methods.

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);
    }
  }
}

...

Note that this compliant solution still requires synchronization, because without it, the test to prevent overflow and the increment will not happen atomically, so two threads calling increment() can still cause overflow. The synchronization block is smaller, and does not include the lookup or addition of new values, so it has less of an impact on performance as the previous compliant solution.

Code Block
bgColor#ccccff

final class KeyedCounter {
  private final ConcurrentMap<String, AtomicInteger> map =
      new ConcurrentHashMap<String, AtomicInteger>();
  private final Object lock = new Object();

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

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

    synchronized (lock) {
      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 ...
}

...

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

VNA03-J

low

probable

medium

P4

L3

Automated Detection

Some static analysis tools are capable of detecting violations of this rule.

 ToolVersion Description 
ThreadSafe
Include Page
ThreadSafe_V
ThreadSafe_V
Implemented

Related Guidelines

MITRE CWE

CWE-362. Concurrent execution using shared resource with improper synchronization ("race condition")

 

CWE-366. Race condition within a thread

 

CWE-662. Improper synchronization

...

[API 2006]

 

[Goetz 2006]

Section 4.4.1, Client-side Locking

 

Section 5.2.1, ConcurrentHashMap

[JavaThreads 2004]

Section 8.2, Synchronization and Collection Classes

[Lee 2009]

Map & Compound Operation

 

      07. Visibility and Atomicity (VNA)