JDK中提供了大量的线程安全类,我们在开发的过程中应该尽可能的重用这些线程安全类,将自己程序中的可变状态委托给底层实现。但是,并不是所有JDK提供的线程安全类都能满足我们的需求,于是我们要进行相应的扩展,本文介绍了一些扩展方法从而满足线程安全需求。
让我们来看一个示例:
我们有一个Vector集合,要求做到当向集合中添加元素时首先判断集合中是否存在该元素,不存在的话则加入,否则忽略,即Put If Absent
Vector本身是线程安全的,但是对于putIfAbsent这种复合操作,并不是线程安全的,我们需要自己实现线程安全的该方法,有以下几种实现
扩展实现该方法
class ThreadSafeVector<T> extends Vector<T> {
public synchronized boolean putIfAbsent(T obj) {
if (contains(obj)) {
return false;
}
return add(obj);
}
}
通过扩展我们实现了线程安全的putIfAbsent方法,但是这种方法会将同步策略分布到不同的类中,如果底层改变了加锁策略,那么子类的线程安全性将被破坏
对于Collections.synchronizedList()封装的List方法并不能采用上述方法实现线程安全的putIfAbsent方法,因为子类并不知道客户端传进来的List是否是线程安全的,对于该类型,我们可以采用以下方法实现线程安全
扩展类的功能
class ListHelper<T> {
public final List<T> list = Collections.synchronizedList(new ArrayList<>());
public boolean putIfAbsent(T obj) {
synchronized (list) {
boolean absent = !list.contains(obj);
if (!absent) {
list.add(obj);
}
return absent;
}
}
}
该方法需要注意的是putIfAbsent方法的监视器必须要和底层的加锁策略保持一致,因此上述方法的同步策略监视器是list,而不能把synchronized放在方法上,否则ListHelper与list的锁不一致导致该类并非是线程安全的
该方法仍然是存在缺陷的,通过客户端扩展类的功能实现线程安全类会导致将原有类的加锁代码放到了另外一个类中,该方法与上述扩展实现都存在一个问题,即将派生类的行为与基类的实现耦合起来
组合
class ThreadSafeList<T> implements List<T> {
private final List<T> list;
ThreadSafeList(List<T> list) {
this.list = list;
}
public synchronized boolean putIfAbsent(T obj) {
boolean absent = !list.contains(obj);
if (absent) {
list.add(obj);
}
return absent;
}
@Override
public synchronized void clear() {
list.clear();
}
}
ThreadSafeList通过自身的内置锁实现了线程安全,它并不关心底层的List是否是线程安全的,虽然造成了轻微的性能损失,但是带来了健壮性,它能给确保线程安全(与Collections.synchronizedList()一样,客户端需要通过ThreadSafeList操作底层List)