java并发编程(三): 对象的组合

对象的组合:

  • 如何将现有的线程安全组件,组合成咱们想要的更大规模的程序。

设计线程安全的类:

  • 设计线程安全类的三个要素:

       1.找出构成对象状态的全部变量; java

       2.找出约束状态变量的不变性条件; 安全

       3.创建对象状态的并发访问管理策略。 并发

收集同步需求:

  • 若是不了解对象的不变性条件后验条件,那就不能确保线程安全性。要知足在状态变量的有效值状态转换上的各类约束条件,就须要借助原子性封装性

依赖状态的操做:

  • 若是在某个操做中包含有基于状态的先验条件,那么这个操做就称为依赖状态的操做。如在操做前,判断当前状态是否能够进行当前操做。

状态的全部权:

  • 全部权封装性老是相互关联的:对象封装它拥有的状态,即对它封装的状态拥有全部权,固然全部权能够经过传递对象,变成共享全部权

实例封闭:

  • 将数据封装在对象内部,能够将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁。
/**
 * 这里将mySet实例封闭在PersonSet中,
 * 尽管HashSet是非线程安全类,
 * 因为mySet是私有且不会逸出的,
 * 咱们经过公共接口提供给外部访问,但加上了PersonSet内置锁保护synchronized,
 * 于是PersonSet是一个线程安全的类
 */
@ThreadSafe
public class PersonSet {
	private final Set<Person> mySet = new HashSet<>();
	
	public synchronized void addPerson(Person p){
		mySet.add(p);
	}
	
	public synchronized boolean containsPerson(Person p){
		return mySet.contains(p);
	}
}
  • 封闭机制更易于构造线程安全的类,由于当封闭类的状态时,在分析类的线程安全性时就无须检查整个程序。

Java监视器模式:

  • Java监视器模式的对象会把对象的全部可变状态都封装起来,并由对象本身的内置锁来保护。如Vector, Hashtable等类;
  • 咱们也能够经过私有锁来代替内置锁:
public class PrivateLock {
	private final Object lock = new Object();
	
	public void methodOne(){
		synchronized(lock){
			// do sth.
		}
	}
}

线程安全性的委托:

  • 多个线程安全的类组合成的类,不必定就是线程安全的。
/**
 * 委托的PersonSet
 * 将内部操做委托给线程安全的类SynchronizedSet
 * 从而自身也是线程安全的
 */
@ThreadSafe
public class DelegatingPersonSet {
	private final Set<Person> mySet = 
			Collections.synchronizedSet(new HashSet<Person>());
	
	public void addPerson(Person p){
		mySet.add(p);
	}
	
	public boolean containsPerson(Person p){
		return mySet.contains(p);
	}
}

独立的状态变量:

  • 咱们还能够将线程安全性委托给多个状态变量,只要这些状态变量彼此独立(不相关):

/**
 * 将线程安全性委托给多个彼此独立的状态变量
 * VisualComponent使用CopyOnWriteArrayList(线程安全)来保存监听器列表
 * keyListeners, mouseListeners彼此独立
 * 所以VisualComponent线程安全
 */
public class VisualComponent {
	private final List<KeyListener> keyListeners = 
			new CopyOnWriteArrayList<>();
	private final List<MouseListener> mouseListeners = 
			new CopyOnWriteArrayList<>();
	
	public void addKeyListener(KeyListener keyListener){
		keyListeners.add(keyListener);
	}
	
	public void removeKeyListener(KeyListener keyListener){
		keyListeners.remove(keyListener);
	}
	
	public void addMouseListener(MouseListener mouseListener){
		mouseListeners.add(mouseListener);
	}
	
	public void removeMouseListener(MouseListener mouseListener){
		mouseListeners.remove(mouseListener);
	}
}

当委托失效时:

  • 当类内部多个状态变量,他们之间存在不变性条件,即便这些状态变量各自是线程安全的,那么该类不必定就线程安全:
/**
 * NumberRange不足以保护它的不变性条件
 * 并发环境下不安全
 */
 @NotThreadSafe
public class NumberRange {
	//不变性条件: lower <= upper
	private final AtomicInteger lower = new AtomicInteger();
	private final AtomicInteger upper = new AtomicInteger();
	
	public void setLower(int i){
		if (i > upper.get()){ //不安全的检查
			throw new IllegalArgumentException("lower can't > upper");
		}
		lower.set(i);
	}
	
	public void setUpper(int i){
		if (i < lower.get()){ //不安全的检查
			throw new IllegalArgumentException("lower can't > upper");
		}
		upper.set(i);
	}
}
  • 若是一个类是由多个独立线程安全的状态变量组成,而且在全部的操做中都不包含无效状态转换,那么能够将线程安全性委托给底层的状态变量。

发布底层的状态变量:

  • 若是一个状态变量是线程安全的,而且没有任何不变性条件来约束它的值,在变量的操做上也不存在任何不容许的状态转换,那么就能够安全地发布这个变量,例如发布上面VisualComponent的keyListeners, mouseListeners。

在现有的线程安全类中添加功能:

  • 经过扩展类,来添加功能。
/**
 * 经过扩展实现非重复Vector
 */
public class NoRepeatVector<E> extends Vector<E> {
	public synchronized boolean putIfAbsent(E e){
		boolean exist = contains(e);
		if (!exist) 
			add(e);
		return exist;
	}
}

客户端加锁机制:

  • 客户端加锁:对于使用某个对象X的客户端代码,使用X自己用于保护其状态的锁来保护这段客户端代码。
/**
 * 这段客户端代码看似线程安全,
 * 但其实并不安全,由于锁住的对象不正确,
 * 这里仅是锁住ListHelper对象,但list对象并无被锁住,
 * 其余客户端仍可在不安全状况下对list进行操做
 */
@NotThreadSafe
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;
	}
}
因此上面的代码,咱们应该对list加锁,而不是ListHelper对象:
@ThreadSafe
public class SafeListHelper<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;
		}
	}
}

组合:

  • 当为现有的类添加一个原子操做时,有一种更好的方法:组合(Composition)。
/**
 * 经过组合实现"若没有则添加" 下午4:48:42
 */
@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 t){
		boolean absent = !list.contains(t);
		if (absent)
			list.add(t);
		return absent;
	}

	@Override
	public synchronized int size() {
		return list.size();
	}
       ...
}

将同步策略文档化:

  • 在文档中说明客户代码须要了解的线程安全性保证,以及代码维护人员须要了解的同步策略

不吝指正。 ide

相关文章
相关标签/搜索