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

对象的组合:

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

设计线程安全的类:

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

1.找出构成对象状态的所有变量;

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

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

收集同步需求:

如果不了解对象的不变性条件与后验条件,那就不能确保线程安全性。要满足在状态变量的有效值或状态转换上的各种约束条件,就需要借助原子性和封装性。

依赖状态的操作:

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

状态的所有权:

所有权与封装性总是相互关联的:对象封装它拥有的状态,即对它封装的状态拥有所有权,当然所有权可以通过传递对象,变成共享所有权。

实例封闭:

将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁。

/**

* 这里将mySet实例封闭在PersonSet中,

* 尽管HashSet是非线程安全类,

* 由于mySet是私有且不会逸出的,

* 我们通过公共接口提供给外部访问,但加上了PersonSet内置锁保护synchronized,

* 因而PersonSet是一个线程安全的类

*/

@ThreadSafe

public class PersonSet {

private final Set 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 mySet =

Collections.synchronizedSet(new HashSet());

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 keyListeners =

new CopyOnWriteArrayList<>();

private final List 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 extends Vector {

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 {

public List list =

Collections.synchronizedList(new ArrayList());

public synchronized boolean putIfAbsent(E x){

boolean absent = !list.contains(x);

if (absent)

list.add(x);

return absent;

}

} 所以上面的代码,我们应该对list加锁,而不是ListHelper对象:

@ThreadSafe

public class SafeListHelper {

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;

}

}

}

组合:

当为现有的类添加一个原子操作时,有一种更好的方法:组合(Composition)。

/**

* 通过组合实现"若没有则添加" 下午4:48:42

*/

@ThreadSafe

public class improvedList implements List {

private final List list;

public improvedList(List 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();

}

...

}

将同步策略文档化:

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

不吝指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值