Java并发编程实战~笔记~章四

4.2 实例封闭

如果某个对象不是线程安全的,可以通过多种技术使其在多线程程序中安全地使用。比如线程封闭,或者通过一个锁来保护对该对象的所有访问。

实例封闭,就是把一个对象封装到另一个对象里面去,能够访问被封装对象的所有代码路径都是已知的,只要在这些路径上面设置合适的同步策略,就能确保以线程安全的方式来使用非线程安全的对象。

比如这个例子:

public class PersonSet {
	private final Set<Person> mySet = new HashSet<Person>();

	public synchronized void addPerson(Person p) {
		mySet.add(p);
	}

	public synchronized boolean containsPerson(Person p) {
		return mySet.contains(p);
	}
}
mySet不是线程安全的,但是它被封闭在PersonSet中不会逸出,唯一能访问mySet的代码路径是addPerson和containsPerson,它们被锁保护了,因此PersonSet是线程安全的。

Collections.synchronizedMap就是用的这种包装方式,这个静态方法在Map外层封装了一个具有访问控制的Map对象,从而使得Map变成了线程安全的。

4.3 线程安全性的委托

一个类可能设计为需要存储一些共享变量,为了实现这个类的线程安全性,可以向上面的实例封闭一样,添加一个线程安全层(也可以自己做这个安全层),或者可以尝试把线程安全性委托给已有的线程安全类。

实例封闭(添加安全层)的做法:

public class VisitRecord {
	int visitTime = 0;

	public synchronized void increase() {
		visitTime++;
	}
}
安全性委托的做法:

public class VisitRecord {
	AtomicInteger visitTime = new AtomicInteger(0);

	public void increase() {
		visitTime.incrementAndGet();
	}
}
经常被用作安全性委托的线程安全的类有:ConcurrentMap、BlockingQueue、Atomic系列等等。

如果有多个共享变量,那么就分两种情况:

1、共享变量间独立,那么仍然可以将每个共享变量使用线程安全类来保存。

2、共享变量间有不变性约束,那么仅靠委托就不足以实现线程安全性,这个类必须提供自己的加锁机制,以保证这些维护不变性条件的复合操作成为原子操作。

要点:

如果一个类是由多个独立且线程安全的状态变量组成的,那么可以将线程安全性委托给底层的状态变量。

如果一个状态变量时线程安全的,并且没有任何不变性条件来约束它的值,那么就可以安全地发布这个变量。

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

四种方式的对比:

1、在原有的线程安全类中添加新功能。如果这些线程安全类是java api,那么显然不能做到。如果是自己工程内部实现的类,那么这将是最好的选择。

如果很清楚线程安全的类,是用的this锁的话,还可以这样:

2、类扩展,添加同步方法。这样做的坏处是,同步策略被分散了,如果底层的类修改了同步策略,或者选择了不同的锁来保护它的状态变量,那么子类会被破坏。

3、客户端加锁。和类扩展是一样的,只是组合了底层对象,并且在新的方法里面,显式的使用了synchronized(底层对象)。缺点同上。

或者不依赖旧类的线程安全性,添加自己的线程安全层:

4、组合。将底层对象作为自己的属性,并且进行实例封闭(不管它是不是线程安全的)。这种方法的好处就是不再有任何安全性的依赖。坏处就是相当于完全重写了同步策略,工作量上去了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值