本节内容摘自《Java 并发编程实践》。
问题:向一个线程安全的类中添加一个线程安全的方法。
Solution1.
修改原始的类,在原始类中进行新的方法编写并且加上锁。
当我们无法修改原始的类,我们应该怎么办呢?
Solution2.
扩展这个类。如果在设计的时候这个类考虑了可扩展性。比如BetterVector 扩展 Vector;
//正确的实现
@ThreadSafe
public class BetterVector<E> extends Vector<E> {
public synchronized boolean putIfAbsent(E x) {
boolean absent = !contains(x);
if(absent) {
add(x);
}
return absent;
}
}
情况更加糟糕一点,原始类无法修改,无非扩展,怎么处理?
Solution3.
客户端加锁机制。:对一个Colletions.synchronizedList封装的ArrayList添加一个方法:若没有则添加。
Code1、
public class ListHelper<E> {
private List<E> list = Collections.synchronizedList(new ArrayList<E>());
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
我们知道,对于list,它是一个线程安全的。并且它持有的锁就是它本身list。
这个方法对不对呢?
考虑下下面的情况。
如果这个list被逸出到一个外部的类。当此外部类调用了put()方法时。试想下面一种情况:
当ListHelper类的对象执行到list.contains(x)时候,此时判断没有添加,往后执行。就在此时,外部类调用了list.add(x);此时就会发生问题。
而已,上面这种写法是不正确的。
所以,正确的写法如下:
//正确的实现
@ThreadSafe
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(<new ArrayList<E>());
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}