1.设计线程安全类需要考虑的因素?
找出构成对象状态的所有变量。
找出约束状态变量的不变性条件。
建立对象状态的并发访问管理策略。
注意: 不变性条件——是指变量的取值范围。后验条件——是指状态改变的时候值是否合法。
@ThreadSafe public final class Counter { @GuardeBy("this") private long value = 0;//Counter类的状态变量 public synchronized long getValue() { //使用了内置的锁机制,原子性&可见性 return value; } public synchronized long increment() { if (value == long.MAX_VALUE) {//状态的不变性条件 throw new IllegalStateException("Counter overflow") } return ++value; } }
不变的对象 一定是线程安全的,除非需要必要的可见性,否则应将所有的域都声明为私有的 ;除非某个域是可变的,否则应将其声明为final域 。这是良好的编程习惯。
2.如何在多线程中访问不是线程安全的对象?
使用同步策略,同步策略定义如何在不尾部对象不变性条件和后验条件的情况下对其状态的访问操作进行协同。同步策略规定了如何将不可变性 、线程封闭和加锁机制 等结合起来以维护线程的安全性。
3.对比几种以安全的方式访问非线程安全对象?
线程封闭 :确保非线程安全的对象只能由单个线程访问。
作为一个类的私有成员(将对象封闭在类的一个实例)。
作为一个局部变量(将对象封闭在某个作用域)。
封闭在一个线程内(某个线程将对象从一个方法传递到另一个方法,而不要在多个线程之间共享对象)。
注意: 被封闭的对象一定不能超过约定的作用域。
《程序员的自我修养》提及一个观点,”计算机科学领域的任何问题, 都可以通过添加一个中间层来解决“ 。当时不懂,现在看来绝对是真理。
遵循Java监视器模式 的对象会把对象的所有可变状态都封装起来,这也是线程封闭的一个使用。java监视器模式在从头开始构建一个类,或者多个非线程安全的类组合为一个类 时非常有用。
线程安全性的委托 :将线程的安全性委托给一个状态变量(或对象),利用这个状态变量的线程安全性来保证对象线程安全的需求。
注意: 如果一个类是由多个独立且线程安全的状态变量组成,并且在所有的操作中都不包含无效的状态转移(不变条件) ,那么可以将线程安全性委托给底层的状态变量。(这种方式目前还不太清楚,后面弄懂来补?)
在现有的线程安全类中添加功能 :java类库包含许多有用的“基础模块”类。通常,应该优先选择重用这些现有的类而不是创建新的类。
需求描述: 需要一个线程安全的链表,提供一个原子的“若没有则添加(Put-If-Absent)” 。同步的List类已经提供了大部分的功能。
实现方案:
添加一个新的原子操作 ,最安全的方式是修改原始的类,但是由于无法访问或修改类的源码这通常无法做到。
通对对类本身进行扩展,比如继承一个子类 ,并非所有类都将状态向子类公共(Vector将状态向子类公开),同时基类同步策略的改变将影响子类。
扩展类的功能,但并不是扩展类本身,而是将扩展代码放入一个“辅助类”中 :客户端加锁同样会破坏同步策略的封装性。
eg:通过客户端加锁来实现“若没有则添加”
@ThreadSafe
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 } } }