本文共 4082 字,大约阅读时间需要 13 分钟。
主要介绍一些组合模式,使得一个类更容易成为线程安全的类,在维护这些类时不会无意中破坏类的安全性保证。
设计线程安全类的过程中,需要包含以下三个基本要素:
对象的状态
同步策略规定了如何将不变性条件、线程封闭和加锁机制结合起来以维护线程的安全性,并且规定了哪些变量由哪些锁来保护。
**尽量多的使用final域。**final类型的域使用的越多,状态空间就越小,越能简化对象可能状态的分析过程。
在单个或多个域上,某一个操作如果存在的无效的状态转换,需要对该操作进行同步。无效的状态转换包括不满足:
如果某个操作存在无效的状态转换,那么该操作必须是原子的,即需要同步。
某些方法包含一些先验条件才能执行,例如:不能够从空队列中删除一个值。单线程程序中如果遇到无法满足先验条件的情况可以直接返回失败,但是并发程序中先验条件可能因为其他线程的执行而变成真,因此要一直等待先验条件为真再执行。
所有权在Java中只是一个设计中的要素,在语言层面没有明显的变现。所有权意味着控制权,如果发布了某个可变对象的引用,则意味着共享控制权。在定义哪些变量构成对象的状态时,只考虑对象拥有的数据。
同步的实现手段主要有三个:
* 对象封闭 * 委托线程安全性给底层状态变量 * 现有类的封装当一个对象封闭在另一个对象中时,由于被访问的对象所有代码路径都是已知的,因此更易于对代码进行分析。
被封闭的作用域可以是:
@GuardedBy("this") private final SetmySet = new HashSet ();public synchronized void addPerson(Person p) { mySet.add(p);}public synchronized boolean containsPerson(Person p) { return mySet.contains(p);}
需要注意的是,在这个实现中,需要Person也是一个线程安全的类,否则一样会线程不安全。也需要注意,不应该让一个本该封闭的对象逸出作用域。
Java中很多容器比如ArrayList和HashMap不是线程安全的类,但类库通过Collections.synchronizedList及类似方法产生一个容器的“装饰器”,使其访问变成了线程安全的。
遵循Java监视器模式的对象会把对象的所有可变状态都封装起来,并由自己的内置锁保护。
public final class Counter { @GuardedBy("this") private long value = 0; public synchronized long getValue() { return value; } public synchronized long increment() { if (value == Long.MAX_VALUE) throw new IllegalStateException("counter overflow"); return ++value; }}
Counter中封装了一个变量value,对该变量的所有访问都需要通过Counter的方法执行,这些方法都是同步的。
Java监视器仅仅是一种编码约定。
public class PrivateLock { private final Object myLock = new Object(); @GuardedBy("myLock") Widget widget; void someMethod() { synchronized (myLock) { // Access or modify the state of widget } }}
实现过程中,使用私有锁而不是对象的内置锁可以避免让客户代码错误的得到锁。
如果一个类是由多个独立且线程安全的状态变量组成,所有操作中都不会破坏已有的限制条件,那么可以将线程安全性委托给底层的状态变量去做。如果某个类含有复合操作,即多个状态变量之间相互影响,仅仅委托给底层的状态变量去做就不足以保证线程安全性,需要在该类中加锁以保证线程安全性。
发布底层的状态变量 如果一个状态变量是线程安全的,并且没有任何不变性条件约束他的值,也不存在任何不允许的状态转换,那么就可以安全的发布这个变量。例如Counter,value需要是正值(约束条件)且必须递增(如果发布客户可以减少,存在不允许的状态转换),因此不能安全的发布Counter中的value状态。
public class VisualComponent { private final ListkeyListeners = new CopyOnWriteArrayList (); private final List mouseListeners = new CopyOnWriteArrayList (); public void addKeyListener(KeyListener listener) { keyListeners.add(listener); } public void addMouseListener(MouseListener listener) { mouseListeners.add(listener); } public void removeKeyListener(KeyListener listener) { keyListeners.remove(listener); } public void removeMouseListener(MouseListener listener) { mouseListeners.remove(listener); }}
将VisualComponent里面的keyListeners和mouseListeners发不出去是安全的。因为没有在其监听器列表的合法状态上施加任何约束,这些域发布不会破坏线程安全。
给现有的类增加新的功能并保持线程安全性有两种方式。
以给一个list增加putIfAbsent功能为例:
class GoodListHelper{ public List list = Collections.synchronizedList(new ArrayList ()); public boolean putIfAbsent(E x) { synchronized (list) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } }}
public class ImprovedListimplements List { private final List list; /** * PRE: list argument is thread-safe. */ public ImprovedList(List list) { this.list = list; } public synchronized boolean putIfAbsent(T x) { boolean contains = list.contains(x); if (contains) list.add(x); return !contains; } // Plain vanilla delegation for List methods. // Mutative methods must be synchronized to ensure atomicity of putIfAbsent. ...}
转载地址:http://eywci.baihongyu.com/