...
Code Block |
---|
|
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 |
---|
|
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 |
---|
|
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 |
---|
|
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 |
---|
|
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: |
...