code.7z

一、概述

通常任务执行过程都是同步进行的,来看一个简单同步执行代码

  1. // ① 获取数据,耗时较长,阻塞
  2. Object data = getData();
  3. // ② 其他事务1
  4. doOther1();
  5. // ③ 其他事务2
  6. doOther2();
  7. // ④ 处理 ① 获取到的结果
  8. doProcessData();

在上面伪代码中,①,②,③ ,④ 按照顺序进行执行,并且每一个步骤都需要等待上一个步骤完成,才能执行。

现在假定每一个步骤的执行耗时如下:

  • ① 耗时 10s
  • ② 耗时 5s
  • ③ 耗时 4s
  • ④ 耗时 3s

则执行耗时时间线如下:
01.png
上述伪代码中 ,① 和 ④ 存在关联性,但是和 ②,③ 与 ①,④ 之间互不关联,不过 ②,③ 需要等待 ① 执行完成才能执行。

现在有一个需求:在代码执行过程中 ① 不进行同步阻塞,在调用 ① 之后直接进行返回,然后执行 ②,③ ,最后执行 ④ 时,由于与 ① 存在关联,等待 ① 执行完成,然后执行 ④。

通过上述方案,减少 ① 的等待时间,提高程序运行效率。

通过 Future 设计模式能够实现上述需求

Future 实现的原理图,如下:
image.png
简单剖析步骤:

step1、方法调用 ① 获取数据,直接创建一个 Thread 后台进行执行,然后返回一个 Future 用以查询执行状态(《观察者模式监控线程生命周期》)

step2、在执行 ②,③ 操作时,后台同样有一个线程在执行 ①

step3、当执行 ④ ,需要一来到 ① 获取的结果时,通过 Future 获取结果。

  • 如果 Thread 后台执行完成了,则 Future 能够直接拿到执行结果
  • 如果 Thread 后台执行未完成,则进行阻塞,等待 Thread 执行完成

在引入了 Future 设计后,执行时间线如下:
02.png
如上图,整体的运行提高了 9 s。

二、代码实现案例

回顾一下上述的伪代码

// ① 获取数据,耗时较长,阻塞
Object data = getData();
// ② 其他事务1
doOther1();
// ③ 其他事务2
doOther2();
// ④ 处理 ① 获取到的结果
doProcessData();

2.1、常规实现

实现上述案例,代码结构图如下:
05.png
执行逻辑

用户通过 FutureService#submit 提交执行任务,得到一个 Future 凭证,当需要得到执行结果时通过 Future#get 获取执行结果。

通过实现,执行逻辑优化为

// ① 获取数据,耗时较长,异步执行
Future future = FutureService.submit(getData());
// ② 其他事务1
doOther1();
// ③ 其他事务2
doOther2();
// ④ 通过 Future 获取 ① 的执行结果,然后执行数据处理
Object data = future.get();
doProcessData();

2.2、带有回调的 Future 设计

上述代码中,虽然 ① 通过异步的方式提高了执行效率,但是仍然需要等待 ① 执行结束,然后进行
④ 的处理。

优化:提供回调方法,当 ① 异步执行完成后,主动调用 ④ ,而无需代码主动去调用。

优化后的伪代码如下:

// ① 获取数据,耗时较长,异步执行,同时提供回到方法 doProcessData()/也就是 ④
Future future = FutureService.submit(getData(),doProcessData());
// ② 其他事务1
doOther1();
// ③ 其他事务2
doOther2();

2.3、演示代码

没有回调时的演示

03.png

有回调时的演示

04.png