Java 类库中包含许多有用的”基础模块“类。通常,我们应该优先选择重用这些现有的类而不是创建新的类。:重用能降低开发工作量、开发风险(因为现有类都已经通过测试)以及维护成本。有时候,某个线程安全类能支持我们需要的所有操作,但更多的时候,现有的类只能支持大部分的操作,此时就需要在不破坏线程安全的情况下添加一个新的操作。
假设我们需要一个线程安全的链表,他需要提供一个原子的”若没有则添加(Put-If-Absent)“的操作。同步的 List 类已经实现了大部分的功能,我们可以根据它提供的 contains 方法和 add 方法构造一个实现。
可以有四种方法来实现这个原子操作。
但这通常是无法做到的,因为你可能无法访问或修改类的源代码。要想修改原始的类,就需要深刻理解代码中的同步策略,这样增加的功能才能与原有的设计保持一致。如果直接将新方法添加到类中,那么意味着实现同步策略的所有代码仍然处于一个源代码文件中,从而更容易理解与维护。
例如,我们可以设计一个 BetterVector 对 Vector 进行扩展,并添加了一个新方法 putIfAbsent。
public class BetterVector<E> extends Vector<E>{ public synchronized boolean putIfAbsent(E x){ boolean absent = !contains(x); if(absent)add(x); return absent; } }
”扩展“方法比较脆弱,主要原因是 同步策略的实现被分离到了多个源代码文件中,如果底层类改变了同步策略,更改了不同的锁来保护状态,那么子类便会被破坏。
对于某些类,比如 Collections.synchronizedList 封装的 ArrayList , 前两种方法都行不通,因为客户代码不知道在同步封装器工厂方法中返回的 List 对象的类型。这时候采用客户端加锁的方式,将扩展代码放到一个”辅助类“中。
于是我们很自然的就写出 ListHelper 辅助类。
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 ,但是它却是在 ListHelper 上加锁,而 List 却是用自己或内部对象的锁。 ListHelper 只是带来了同步的假象,
在 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;} } }
public class ImprovedList<T> implements List<T> { public final List<T> list; public ImprovedList(List<T> list) { this.list = list; } public synchronized boolean putIfAbsent(T x) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } //...按照类似的方式委托List的其他方法 }
Java 并发编程(三)为现有的线程安全类中添加新的原子操作
原文地址:http://blog.csdn.net/zq602316498/article/details/40221801