1.扩展Vector并增加一个“若没有则添加”方法
public class BetterVector<E> extends Vector<E> {
public synchronized boolean putIfAbsent(E x) {
boolean absent = !contains(x);
if (absent)
add(x)
return absent;
}
}
扩展方法比直接将代码添加到类中更脆弱,因为现有的同步策略实现被分布到多个单独的源代码文件中。如果地层的类改变了同步策略并选择了不同的锁来保护它的状态变量,那么子类会被破坏。因为在同步策略改变后它无法再使用正确的锁来控制对基类状态的并发访问。
1.2 客户端加锁机制
1.2.1错误的实现方式:
public class ListHelper<E> {
public 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;
}
}
为什么这种方式不能实现线程安全性?
毕竟,putIfAbsent已经声明为synchronized类型的变量,对不对?问题在于在错误的锁上进行了同步。无论List使用哪一个锁来保护它的状态,可以确定的是,这个锁并不是ListHelper上的锁。ListHelper只是带来了同步的假象,尽管所有的链表操作都被声明为synchronized,但却使用了不同的锁,这意味着putIfAbsent相对于List的其他操作来说并不是原子的,因此就无法确保当putIfAbsent执行时另一个线程不会修改链表。
1.2.2正确实现方式
要想使这个方法能正确执行,必须使List在实现客户端加锁或外部加锁时使用同一个锁。客户端加锁是指,对于使用某个对象X的客户端代码,使用X本身用于保护其状态的锁来保护这段客户端代码。要使用客户端加锁,你必须知道对象X使用的是哪一个锁。
在Vector和同步封装器类的文档中指出,它们通过使用Vector或封装器容器的内置锁来支持客户端加锁。
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;
}
}
}