多线程设计模式解读—Promise(承诺)模式
2018-08-12 本文已影响114人
九九派
上次我们讲到多线程设计模式的Guarded Suspension(保护性暂挂模式),Guarded Suspension是条件未满足时线程一直处于等待状态,直到条件满足才继续运行,而在Promise模式中,Promise的getResult方法获取异步任务结果,如果任务未执行完毕,就一直处于等待状态,可以说,Promise模式是Guarded Suspension模式的一个应用实例,它有两个重要角色:Promise,主要用于包装异步任务处理结果;Promisor,用于对外提供返回Promise的异步方法,并启动异步任务。而这里Promise可以直接用JDK 中的Future实现。
我们先来看一段源码:
public class FutureTaskMain {
public static void main(String[] args){
//初始化长时计算器
Future<Integer> calculatorPromise = Calulator.newInstance();
System.out.println("模拟主线程任务执行开始...");
System.out.println("模拟主线程任务执行结束...")
//获取执行结果,
try {
//异步操作被封装在了Calculator中
Integer result = (Integer) calculatorPromise.get(6, TimeUnit.SECONDS);
System.out.println("长时任务执行完成,结果:"+result);
} catch (InterruptedException e) {
// 守护线程阻塞被打断;
e.printStackTrace();
} catch (ExecutionException e) {
// 执行任务时出错;
e.printStackTrace();
} catch (TimeoutException e) {
// 执行超时
calculatorPromise.cancel(true);
e.printStackTrace();
} catch (CancellationException e) {
//如果线程已经cancel了,再执行get操作会抛出这个异常
e.printStackTrace();
}
}
}
class Calulator{
public static Future<Integer> newInstance() {
//创建计算任务,传入FutureTask
final FutureTask futureTask = new FutureTask(new Callable<Integer>(){
@Override
public Integer call() throws Exception {
System.out.println("模拟长时间计算任务执行中...");
Thread.sleep(5000);
Random rand = new Random();
Integer i = rand.nextInt(900) + 100;
return i;
}
});
new Thread(futureTask).start();
return futureTask;
}
}
这里FutureTask就是Promise角色,主要用于包装异步任务处理结果,而Calculator是Promisor角色,我们可以看到,Promise模式屏蔽了同步和异步编程的差异,异步操作被封装在了Calculator中,客户端代码像调用同步方法一样直接调用即可,无需关注内部的技术细节。
有两个需要注意的地方:
1、异常的处理
你希望知道Promise执行过程中是否会抛出异常,而它是运行在异步方法中的,Promisor方法并不知道,解决方法是将异常记录在Promise的实例变量中,在返回时检查抛出,不过FutureTask已经帮我们实现了这一步,我们只要处理get返回时抛出的异常即可。
2、访问过多时会产生大量的线程,增加系统的开销和资源的消耗,因此,可以考虑用FutureTask + ExecutorService的方式,如:
ExecutorService executorService=Executors.newFixedThreadPool(10);
executorService.execute(futureTask);