Versions Compared

Key

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

...

Code Block
bgColor#FFCCCC
final class IPHolder {
  private final List<InetAddress> ips = Collections.synchronizedList(new ArrayList<InetAddress>());
  
  public void addIPAddress(InetAddress address) {
    // Validate address
    ips.add(address);
  }
  
  public void addAndPrintIPAddresses(InetAddress address) {
    addIPAddress(address);
    InetAddress[] ia = (InetAddress[]) ips.toArray(new InetAddress[0]);      
    // Iterate through array ia ...
  }
}

...

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

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

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

...

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

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

 // Other accessors ...
}

Because the check for integer overflow following the addition is absent, the caller must ensure that the increment() method is called no more than Integer.MAX_VALUE times for any key. Refer to INT00-J. Perform explicit range checking to ensure integer operations do not overflow for more information.

...

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(0);
    AtomicInteger old = map.putIfAbsent(key, value);
   
    if (old != null) { 
      value = old; 
    }

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

  public Integer getCount(String key) {
    AtomicInteger value = map.get(key);
    return value.get();
  }
 // Other accessors ...
}

This compliant solution does not use Collections.synchronizedMap() because locking on the (unsynchronized) map provides sufficient thread-safety for this application. The guideline CON40-J. Do not synchronize on a collection view if the backing collection is accessible provides more information about synchronizing on synchronizedMap objects.

...

Wiki Markup
The previous compliant solution does not scale very well because a class with several {{synchronized}} methods can be a potential bottleneck as far as acquiring locks is concerned, and may further yield a deadlock or livelock. The class {{ConcurrentHashMap}} provides several utility methods tofor performperforming atomic operations and is often a good choice, as demonstrated in this compliant solution \[[Lee 09|AA. Java References#Lee 09]\]. 

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(0);
    AtomicInteger old = map.putIfAbsent(key, value);
   
    if (old != null) { 
      value = old; 
    }

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

  public Integer getCount(String key) {
    AtomicInteger value = map.get(key);
    return value.get();
  }
 // Other accessors ...
}

Wiki Markup
According to Goetz et al. \[[Goetz 06|AA. Java References#Goetz 06]\] section 5.2.1. ConcurrentHashMap:

...