大家好,我是 方圆。最近在开发 延保服务 频道页时,为了提高查询效率,使用到了多线程技术。为了对多线程方案设计有更加充分的了解,在业余时间读完了《图解 Java 多线程设计模式》这本书,觉得收获良多。本篇文章将介绍其中提到的 Future 模式,以及在实际业务开发中对该模式的应用,而这些内容对于本书来说只是冰山一角,还是推荐大家有时间去阅读原书。
1. Future 模式:“先给您提货单”
我们先来看一个场景:假如我们去蛋糕店买蛋糕,下单后,店员会递给我们提货单并告知“请您傍晚来取蛋糕”。到了傍晚我们拿着提货单去取蛋糕,店员会先和我们说“您的蛋糕已经做好了”,然后将蛋糕拿给我们。
如果将下单蛋糕到取蛋糕的过程抽象成一个方法的话,那么意味着这个方法需要花很长的时间才能获取执行结果,与其一直等待结果,不如先拿着一张“提货单”,到我们需要取货的时候,再通过它去取,而获取“提货单”的过程是几乎不耗时的,而这个提货单对象就被称为 Future
,后续便可以通过它来获取方法的返回值。用 Java 来表示这个过程的话,需要使用到 FutureTask
和 Callable
两个类,如下:
public class Example {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 预定蛋糕,并定义“提货单”
System.out.println("我:预定蛋糕");
FutureTask<String> future = new FutureTask<>(() -> {
System.out.println("店员:请您傍晚来取蛋糕");
Thread.sleep(2000);
System.out.println("店员:您的蛋糕已经做好了");
return "Holiland";
});
// 开始做蛋糕
new Thread(future).start();
// 去做其他事情
Thread.sleep(1000);
System.out.println("我:忙碌中...");
// 取蛋糕
System.out.println("我:取蛋糕 " + future.get());
}
}
// 运行结果:
// 我:预定蛋糕
// 店员:请您傍晚来取蛋糕
// 我:忙碌中...
// 店员:您的蛋糕已经做好了
// 我:取蛋糕 Holiland
方法的调用者可以将任务交给其他线程去处理,无需阻塞等待方法的执行,这样调用者便可以继续执行其他任务,并能通过 Future
对象获取执行结果。
它的运行原理如下:创建 FutureTask
实例时,Callable
对象会被传递给构造函数,当线程调用 FutureTask
的 run
方法时,Callable
对象的 call
方法也会被执行。调用 call
方法的线程会同步地获取结果,并通过 FutureTask
的 set
方法来记录结果对象,如果 call
方法执行期间发生了异常,则会调用 setException
方法记录异常。最后,通过调用 get
方法获取方法的结果,注意这里可能会抛出方法执行时产生的异常。
public void run() {
// ...
try {
// “提货任务”
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 调用 callable 的 call 方法
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// 捕获并设置异常
setException(ex);
}
if (ran)
// 为结果赋值
set(result);
}
} finally {
// ...
}
}
protected void set(V v) {
if (STATE.compareAndSet(this, NEW, COMPLETING))