Versions Compared

Key

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

Thread-safety guarantees that no two threads can simultaneously access or modify some shared data. However, if two or more operations need to be performed safely, it becomes necessary to enforce atomicity. It is possible for two threads to read some shared value, independently perform operations on it and induce a race condition while storing the final result. For example, programmers

Programmers sometimes assume that using a thread-safe Collection does not require explicit synchronization which is a misleading thought. It follows that using a thread-safe Collection by itself may does not ensure program correctness unless special care is taken to ensure that the client performs all related and independently atomic operations, as one atomic operation.

Noncompliant Code Example

This noncompliant code example is comprised of comprises an ArrayList collection which is non-thread-safe by default. There is, however, a way around this drawback. Most thread-unsafe classes have a synchronized thread-safe version, for example, Collections.synchronizedList is a good substitute for ArrayList and Collections.synchronizedMap is a good alternative to HashMap. The atomicity pitfall described in the coming lines, remains to be addressed even when the particular Collection offers thread-safety benefits.

...

The operations within a thread's run() method are non-atomic. That is, it is possible for the first thread to operate on data that it does not expect. This noncompliant code's output, consisting of varying array lengths, indicates a race condition between threads. In other words, the statements that are responsible for adding an IP address and printing it out are not sequentially consistent.

...

Compliant Solution

Composition offers some more benefits as compared to the previous solution, although at the cost of a slight performance penalty (refer to OBJ07-J. Understand how a superclass can affect a subclass for details on how to implement composition). This allows the CompositeCollection class to use its own intrinsic lock in a way that is completely independent of the lock of the underlying list class.

Code Block
bgColor
Code Block
bgColor#ccccff
class CompositeCollection implements Runnable {
  private List<InetAddress> ips;
 
  public CompositeCollection(List<InetAddress> list) {
    this.ips = list;
  }
  
  public synchronized void addIPAddress(InetAddress ia) {
    // Validate
    ips.add(ia);
  }

  public void run() {
    try {
      InetAddress[] ia;   
      ia = (InetAddress[]) ips.toArray(new InetAddress[0]);     
      System.out.println("Number of IPs: " + ia.length); 
    } catch (UnknownHostException e) { /* Forward to handler */ }     		
  }
}

Wiki Markup
This approach allows the {{CompositeCollection}} class to use its own intrinsic lock in a way that is completely independent of the lock of the underlying list class. Moreover, this permits the underlying collection to be thread-unsafe because the {{CompositeCollection}} wrapper prevents direct accesses to its methods by exposing its own synchronized equivalents. This approach also provides consistent locking even when the underlying list is not thread-safe or when it changes its locking policy. \[[Goetz 06|AA. Java References#Goetz 06]\]

...