Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Enforced compliance with CON04-J

...

This noncompliant code example uses two AtomicReference objects to hold one BigInteger object reference each.

Code Block
bgColor#FFcccc
public class AtomicAdder {
  private final AtomicReference<BigInteger> first;	
  private final AtomicReference<BigInteger> second; 

  public AtomicAdder(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 as synchronized to guarantee atomicity.

Code Block
bgColor#ccccff

class AtomicAdder {
  // ...

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

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

Prefer using the block form of synchronization when there are nonatomic operations within the method that do not require any synchronization. These operations can be decoupled from those that require synchronization and executed outside the synchronized block.

...

To eliminate the race condition, ensure atomicity by using the underlying list's lock. This can be achieved by including all statements that use the array list within a synchronized block that locks on the list.

Code Block
bgColor#ccccff

class RaceCollection {
  // ...

  public void addIPAddress(InetAddress ia) { 
    synchronized (ips) { 
      // Validate
      ips.add(ia);
    }
  }

public  public void addAndPrintIP() throws UnknownHostException {
    synchronized (ips) {
      addIPAddress(InetAddress.getLocalHost());
      ia = (InetAddress[]) ips.toArray(new InetAddress[0]);           
      System.out.println("Number of IPs: " + ia.length); 
    }
  }
}

Wiki Markup
This technique is also called client-side locking \[[Goetz 06|AA. Java References#Goetz 06]\], because the class holds a lock on an object that presumably might be accessible to other classes. Goetz et al. \[[Goetz 06|AA. Java References#Goetz 06]\] caution against misuse of client-side locking:

...

Code Block
bgColor#FFCCCC
public 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 value = (old == null) ? 1 : old.intValue() + 1;
    map.put(key, value);
  }

  public Integer getCount(String key) {
    return map.get(key);
  }
}

Compliant Solution (synchronized

...

blocks)

Wiki Markup
This compliant solution declares uses a private object lock to synchronize the method bodies of the {{increment()}} and {{getCount}} methods as {{synchronized}}, to ensure atomicity \[[Lee 09|AA. Java References#Lee 09]\]. For more information on private object locks, see [CON04-J. Use the private lock object idiom instead of the Class object's intrinsic locking mechanism].

Code Block
bgColor#ccccff

public class KeyedCounter {
  private final Map<String,Integer> map = new HashMap<String,Integer>();
 
Code Block
bgColor#ccccff

public class KeyedCounter {
  private final Map<String,Integer>Object maplock = new HashMap<String,Integer>Object();
 
  public synchronized void increment(String key) {
    synchronized (lock) {
      Integer old = map.get(key);
      int value = (old == null) ? 1 : old.intValue() + 1;
      map.put(key, value);
    }
  }

  public synchronized Integer getCount(String key) {
    synchronized (lock) {
      return map.get(key);
    }
  }
}

Also, note that this would be a violation of a previously discussed noncompliant code example if the field map were to refer to a Collections.synchronizedMap object. This compliant solution uses the intrinsic lock of the class for all purposes.

...