概述
到目前为止,我们已经介绍了关于线程安全与同步的一些基础知识,然而,我们不希望对每一次内存访问都进行分析以确定程序是线程安全的,而是希望将一些现有的线程安全组件组合为更大规模的组件或程序。
设计线程安全的类
packagechapter4;importcommon.GuardedBy;importcommon.ThreadSafe;/***@authorzhen
* @Date 2018/10/26 11:39*/@ThreadSafepublic final classCounter {
@GuardedBy("this") private long value = 0;public synchronized longgetValue() {returnvalue;
}public synchronized longincrement() {if (value ==Long.MAX_VALUE) {throw new IllegalStateException("counter overflow");
}return ++value;
}
}
使用监视器模式的线程安全计数器例子
通过使用封装技术,可以使得在不对整个程序进行分析的情况下就可以判断一个类是否是线程安全的。
在设计线程安全类的过程中,需要包含以下三个基本要素:
找出构成对象状态的所有变量;
找出约束状态变量的不可变条件;
建立对象状态的并发访问管理策略
1>收集同步需求
如果不了解对象的不变性条件和后验条件,那么就不能确保线程安全性。要满足在状态变量的有效值或状态转换上的各种约束条件,需要借助原子性和封装性
2>依赖状态的操作
类的不变性条件与后验条件约束了再对象上有哪些状态和状态转换是有效的。在某些对象的方法中还包含一些基于状态的先验条件。
在java中,等待某个条件为真的各种内置机制(包括等待和通知机制)都与内置加锁机制紧密关联,要正确使用它们并不容易。想要实现某个等待先验条件为真时才执行的操作,一种更简单的方法是通过现有库中的类(例如阻塞队列Blocking Queue或者信号量Semaphore)来实现依赖状态的行为。
3>状态的所有权
垃圾回收机制使得避免了如何处理所有权的问题
对象封装它拥有的状态反之,对它封装的状态拥有所有权。如果发布了某个可变对象的引用,共享控制权。容器类通常表现出所有权分离的形式。
实例封闭
将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁
封闭机制更易与构造线程安全的类,因为当封闭类的状态时,在分析类的线程安全性检查时就无须检查整个程序
@ThreadSafepublic classPersonSet {
@GuardedBy("this")private final Set mySet = new HashSet<>();public synchronized voidaddPerson(Person p) {
mySet.add(p);
}public synchronized booleancontainsPerson(Person p) {returnmySet.contains(p);
}
}
通过封闭机制确保线程安全例子
1>线程监视器模式:
遵循java监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护
public classPrivateLock {private final Object myLock = newObject();
@GuardedBy("myLock") Widget widget;voidsomeMethod() {synchronized(myLock) {//访问或者修改Widget的状态
}
}
}
通过私有锁保护对象线程安全例子
线程安全性的委托
如果一个类是由多个独立且线程安全的状态变量组成,并且在所有的操作都不包含无效状态转换,那么可以将线程安全性委托给底层的状态变量
如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全地发布这个变量
在现有的线程安全类中添加功能
修改原始类
拓展这个类
将拓展代码放入一个辅助类中
1、客户端加锁机制
2、组合
/***@authorzhen
* @Date 2018/10/31 15:08
* 拓展Vector并增加一个若没有则添加方法*/@ThreadSafepublic class BetterVector extends Vector{public synchronized booleanputIfAbsent(E x) {boolean absent = !contains(x);if(absent) {
add(x);
}returnabsent;
}
}
拓展Vector增加一个若没有则添加例子
/***@authorzhen
* @Date 2018/10/31 15:24
* 非线程安全的“若没有则添加”*/@NotThreadSafepublic class ListHelper{public List list = Collections.synchronizedList(new ArrayList());//这里的锁时ListHelper对象,虽然list使用synchronized保证同步,但是锁不一样
public synchronized booleanputIfAbsent(E x) {boolean absent = !list.contains(x);if(absent) {
list.add(x);
}returnabsent;
}
}
非线程安全的若没有则添加例子
/***@authorzhen
* @Date 2018/10/31 15:32
* 通过客户端加锁来实现“若没有则添加”*/@ThreadSafepublic class ListHelper1{public List list = Collections.synchronizedList(new ArrayList());public booleanputIfAbsent(E x) {synchronized(list) {boolean absent = !list.contains(x);if(absent) {
list.add(x);
}returnabsent;
}
}
}
通过客户端加锁实现若没有则添加例子
/***@authorzhen
* @Date 2018/10/31 15:40
* 通过组合实现若没有则添加*/@ThreadSafepublic class ImprovedList implements List{private final Listlist;public ImprovedList(Listlist) {this.list =list;
}public synchronized booleanputIfAbsent(T x){boolean contains =list.contains(x);if (!contains) {
list.add(x);
}return !contains;
}
@Overridepublic intsize() {returnlist.size();
}
@Overridepublic booleanisEmpty() {returnlist.isEmpty();
}
@Overridepublic booleancontains(Object o) {returnlist.contains(o);
}
@Overridepublic Iteratoriterator() {returnlist.iterator();
}
@OverridepublicObject[] toArray() {returnlist.toArray();
}
@Overridepublic T1[] toArray(T1[] a) {returnlist.toArray(a);
}
@Overridepublic booleanadd(T t) {returnlist.add(t);
}
@Overridepublic booleanremove(Object o) {returnlist.remove(o);
}
@Overridepublic boolean containsAll(Collection>c) {returnlist.containsAll(c);
}
@Overridepublic boolean addAll(Collection extends T>c) {returnlist.addAll(c);
}
@Overridepublic boolean addAll(int index, Collection extends T>c) {returnlist.addAll(index, c);
}
@Overridepublic boolean removeAll(Collection>c) {returnlist.removeAll(c);
}
@Overridepublic boolean retainAll(Collection>c) {returnlist.retainAll(c);
}
@Overridepublic voidclear() {
list.clear();
}
@Overridepublic T get(intindex) {returnlist.get(index);
}
@Overridepublic T set(intindex, T element) {returnlist.set(index, element);
}
@Overridepublic void add(intindex, T element) {
list.add(index, element);
}
@Overridepublic T remove(intindex) {returnlist.remove(index);
}
@Overridepublic intindexOf(Object o) {returnlist.indexOf(o);
}
@Overridepublic intlastIndexOf(Object o) {returnlist.lastIndexOf(o);
}
@Overridepublic ListIteratorlistIterator() {returnlist.listIterator();
}
@Overridepublic ListIterator listIterator(intindex) {returnlist.listIterator(index);
}
@Overridepublic List subList(int fromIndex, inttoIndex) {returnlist.subList(fromIndex,toIndex);
}
}
推荐:通过组合的方式实现若没有则添加例子
将同步策略文档化
在文档中说明客户代码需要了解的线程安全保证以及代码维护人员需要了解的同步策略。