...
This noncompliant code example uses two AtomicReference
objects to hold one BigInteger
object reference each.
Code Block |
---|
|
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 |
---|
|
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 |
---|
|
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 |
---|
|
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 |
---|
|
public class KeyedCounter {
private final Map<String,Integer> map = new HashMap<String,Integer>();
|
Code Block |
---|
|
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.
...