对象组合
设计线程安全的类
- 确定组成 对象状态 的 变量
- 确定 约束对象状态 的 不变式
- 建立 并发访问对象状态 的 规则
后置条件
某些变量的取值是有限制范围的, 改变状态变量之后需要 检查改变后的状态 是否合法, 需要额外同步
前置条件
满足一定的条件 操作才能继续进行:
- wait/notfiy
- blocking queue
- semaphore
对象限制
- 对象限制在 类 范围内:作为 类的私有成员
- 对象限制在 方法 内部:作为 方法的局部变量
- 对象限制在 线程 范围内:只能在 特定线程中访问该对象
monitor模式
public class PrivateLock { private final Object myLock = new Object(); @GuardedBy("myLock") Widget widget; void someMethod() { synchronized(myLock) { // Access or modify the state of widget } } }
使用一个 额外对象加锁 比使用java内置锁更灵活
委托线程安全
- 如果类只包含 一个状态变量 :
- 那么类是否是线程安全的, 取决于 该状态变量是否是线程安全
- 如果类包含 多个状态变量 :
- 如果类中的多个状态变量之间是 相互独立 的, 那么线程安全的责任仍可以 委托给状态变量
- 如果类中的多个状态变量 参与不变式 ,那需要 额外的同步
对现有类添加线程安全
继承现有类
public class BetterVector<E> extends Vector<E> { public synchronized boolean putIfAbsent(E x) { boolean absent = !contains(x); if (absent) add(x); return absent; } }
如果 父类修改 了,就可能 破坏子类的同步性
比如contains的含义变化了
客户端加锁
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; } } }
客户端加锁更糟糕,因为 客户端加锁的策略 很可能和 被加锁的类自身的锁管理策略 相 违背
对象组合
@ThreadSafe public class ImprovedList<T> implements List<T> { private final List<T> list; public ImprovedList(List<T> list) { this.list = list; } public synchronized boolean putIfAbsent(T x) { boolean contains = list.contains(x); if (contains) list.add(x); return !contains; } public synchronized void clear() { list.clear(); } // ... similarly delegate other List methods }
继承父类,把父类对象放进子类的一个 final instance, 使用 子类的内置锁 进行同步