近年来,超线程、多核心等并发技术逐渐进入桌面计算机领域,而计算机硬件的变革也从更快的处理器向更多的处理器方向转变,这一切都要求软件开发者在开发软件时为适应硬件做出更多的改变,在设计软件时,必须要能充分利用硬件提供的并发能力来提升软件性能。但令人惊奇地是并发编程技术仍热停留在过程式编程时代而没有什么大的变化。如果进行并发编程,程序员必须要深入了解线程、锁、竞争条件等等底层机制才有可能写出正确的代码。更高抽象层次的并发编程技术并不是没有,它们才刚刚起步,还没有广泛应用,在所有的这些技术中,活动对象(Active Objects)设计模式就是其中的一种。
活动对象工作在对象级别而不像其他设计模式工作在对象继承层级关系中。它将方法的调用和其实际的执行解耦,方法的执行位于对象本身的控制线程中,因而也不会阻塞调用者。总的说来,活动对象模式有6个元素:
1.代理。以公共方法的方式向客户对象提供访问接口。
2.接口。定义了活动对象上的方法请求。
3.队列。保存来自客户的挂起的请求。
4.调度器。决定从挂起的队列中选取哪个请求来执行。
5.实现。活动对象方法的实现。
6.回调/变量。客户对象用它来获取请求执行的结果,活动方法的执行结果通常被封装到成为future的对象中。该future对象中包含了一个类似于占位符的东西。一旦活动对象完成了方法执行就会将结果放到future的占位符上。但在方法完成之前尝试获取占位符上的内容将会阻塞线程。
从活动对象的这些定义中,我们可以看出它的几个特征:
基于消息机制:对活动对象的请求和可能的参数都 被转化为消息,这些消息被转发给活动对象实际实现并排队等待处理。处理结果以future对象返还给提出请求的对象。
异步调用:对活动对象的请求被异步执行,实际由活动对象的工作线程处理请求,故不会阻塞调用者。仅当请求未完成执行时,调用者试图获取处理结果时会被阻塞。
线程安全:活动对象天生是线程安全的。因为他顺序地从请求队列摘取消息进行处理,并且始终在一个单独的线程中执行,由于不存在资源竞争,所以也不用担心同步、死锁等问题。同步仍旧会发生,但它通过将方法调用排队,使得任何时刻都只能发生一个调用,从而将同步控制在消息的级别上发生。
下面是用Java实现的一个活动对象的示例。
import java.util.*;
import java.util.concurrent.*;
public class ActiveObjectsDemo {
//创建ActiveObjectDemo对象的工作线程,Executors.newSingleThreadExecutor()创建的是单个线程
//所有在ex中执行的方法都在这个线程中串行地逐个执行
private ExecutorService ex=
Executors.newSingleThreadExecutor();
private Random rand=new Random(47);
private void pause(int factor){
try{
TimeUnit.MICROSECONDS.sleep(100+rand.nextInt(factor));
}catch(InterruptedException e){
System.out.println("sleep() interrupted");
}
}
public Future<Integer> calculateInt(final int x ,final int y){
/*
* ExecutorService的submit方法启动的线程会返回一个Future对象
* 当方法执行完毕会将结果保存到该Future对象中。
*/
return ex.submit(new Callable<Integer>() {
public Integer call(){
System.out.println("starting "+x+"+"+y);
pause(2000);
return x+y;
}
});
}
public Future<Float> calculateFloat(final float x ,final float y){
/*
* ExecutorService的submit方法启动的线程会返回一个Future对象
* 当方法执行完毕会将结果保存到该Future对象中。
*/
return ex.submit(new Callable<Float>() {
public Float call(){
System.out.println("starting "+x+"+"+y);
pause(2000);
return x+y;
}
});
}
public void shutdown() {
ex.shutdown();
}
public static void main(String[] args) {
ActiveObjectsDemo demo=new ActiveObjectsDemo();
//使用CopyOnWriteArrayList可以防止并发修改异常(ConcurrentModificationException)
List<Future<?>> results=new CopyOnWriteArrayList<Future<?>>();
/*
* 所有对calculateFloat和calculateInt读调用都是立即返回
* 并且就算是“同时”调用这些方法,它们也会排队串行执行,避免了显示地同步需求
*/
for(float f=0.0f;f<1.0f;f+=0.2f)
results.add(demo.calculateFloat(f, f));
for(int i=0;i<5;i++)
results.add(demo.calculateInt(i, i));
System.out.println("All asynch calls made");
while(results.size()>0){
for(Future<?> f: results){
try{
//阻塞并获取调用结果
System.out.println(f.get());
}catch(Exception exception){
throw new RuntimeException(exception);
}
results.remove(f);
}
}
demo.shutdown();
}
}