什么是Promise模式

一个完整流程的实现,需要执行两个任务,任务A和任务B。
如果任务A(烧水任务)和任务B(准备茶杯,茶叶)需同步执行,即先执行完任务A再执行完任务B,则执行的时间就是任务A+任务B的执行时间。
image.png
基于Promise模式,可以在执行任务B(准备茶叶,茶杯任务)的同时,可以用Promise实现异步执行任务A(烧水任务),即可以在执行任务A的同时执行任务B。然后,在任务A的主线上,如果任务A先于任务B执行结束,并不会立即停止主线流程,而是会阻塞等待任务B的执行结果,等到主线也得到任务B的结果后,这条主线才算执行完成。

image.png

Promise模式介绍

主要由4部分组成,1:Promisor;2:Executor;3:Promise;4:Result
(1)Executor需要在Promisor的create()方法中执行;
(2)create()的返回值是Promise;
(3)Result则是在Promise中的get()方法的返回值。
image.png

Promisor类

Promisor类会对外暴露一个create方法,一旦执行这个create方法,就会发生两件事:
(1)执行器Executor开始异步执行任务;
(2)Promisor会创建并返回一个Promise;
image.png

Executor执行器

在Promisor类中定义一个任务,Executor会异步执行这个任务。

Promise对象

是Promisor的create()方法的返回值,是立即返回。(类似买彩票,返回值就是买彩票拿到的号码。并不能立即知道结果,但也不需要在彩票站等待结果,可以直接拿着号码回去。等到开奖的时间到了,再根据这个号码查询是否中奖。)
isDone()方法:用来表示任务是在执行中还是任务已经完成,但不会告知任务的结果。
get()方法:获取任务执行的结果,在获取结果的时候,需要进行阻塞等待。

没有Promise模式下处理多任务

没有Promise模式下同步处理多任务

image.png
任务执行过程为串行化,必须等待前一个线程执行完,再去执行后一个线程。任务执行的总耗时就是两个线程的执行时间的总和。

没有Promise模式下异步处理多任务

image.png
可能会存在一种现象,异步线程的执行还未完成时,主线程的任务已经完成,就导致主线程执行完毕关闭任务流程,并未等到异步线程任务执行完毕。

基于Promise模式异步处理多任务

代码示例

  1. package com.cmic.test.promise;
  2. import java.util.concurrent.ExecutionException;
  3. import java.util.concurrent.Future;
  4. import java.util.concurrent.FutureTask;
  5. public class Promisor {
  6. public static Future<Object> create(long startTime) {
  7. FutureTask<Object> futureTask = new FutureTask<>(() -> {
  8. System.out.println("开始烧水,当前用时:" + (System.currentTimeMillis() - startTime) + "ms");
  9. BoilWater boilWater = new BoilWater();
  10. Thread.sleep(15000);
  11. boilWater.setStatus(true);
  12. return boilWater;
  13. });
  14. new Thread(futureTask).start();
  15. return futureTask;
  16. }
  17. public static void main(String[] args) throws InterruptedException, ExecutionException {
  18. long startTime = System.currentTimeMillis();
  19. // part1: 使用promise模式异步处理烧水流程
  20. Future<Object> promise = Promisor.create(startTime);
  21. // part2:主线程处理准备茶叶茶杯的流程
  22. System.out.println("开始准备茶叶茶杯,需要3分钟,当前用时:" + (System.currentTimeMillis() - startTime) + "ms");
  23. TeaAndCup teaAndCup = new TeaAndCup();
  24. Thread.sleep(3000);
  25. teaAndCup.setStatus(true);
  26. System.out.println("茶叶茶杯准备完成,当前用时:" + (System.currentTimeMillis() - startTime) + "ms");
  27. // part3:通过promise来获取执行结果
  28. // isDone: 查看任务是否执行完毕
  29. if (!promise.isDone()) {
  30. System.out.println("茶叶茶杯结束,等待烧水完成!");
  31. }
  32. // 获取promise模式下异步处理的结果,在未获得结果时, get() 会一直阻塞
  33. BoilWater boilWater = (BoilWater)promise.get();
  34. System.out.println("获取到烧水完成信号,当前用时:" + (System.currentTimeMillis() - startTime) + "ms");
  35. System.out.println("开始泡茶");
  36. System.out.println("总共耗时:" + (System.currentTimeMillis() - startTime) + "ms");
  37. }
  38. }

输出结果

  1. 开始准备茶叶茶杯,需要3分钟,当前用时:107ms
  2. 开始烧水,当前用时:107ms
  3. 茶叶茶杯准备完成,当前用时:3118ms
  4. 茶叶茶杯结束,等待烧水完成!
  5. 获取到烧水完成信号,当前用时:15119ms
  6. 开始泡茶
  7. 总共耗时:15120ms

基于Promise实现的云盘同步优化

场景描述

本地 和网盘之间的数据同步,需要有两个任务执行完成才可以做数据同步。一是扫描本地文件内容任务,二是本地与网盘之间建立网络连接。只有执行完这两个任务,才可以进行数据同步的操作。
如果网络连接和扫描本地文件两个任务进行串行化执行的话,整个流程就会太慢。
image.png

场景要点明细

(1)用户并不知道有”建立云端网络连接”的过程;
(2)”建立云端网络连接”可能很耗时;
(3)”建立云端网络连接”并不需要在”扫描本地文件”之前先完成;
(4)在数据同步准备工作结束之前,必须得到”建立云端网络连接”的完成反馈。
(5)用户并不关注”建立云端网络连接”这个过程,所以可以将这个过程进行异步处理。但是最终的流程需要等待云端网络连接的建立结果,只有网络建立连接成功,才能算作准备完毕,所以需要在获得结果时主流程进行阻塞等待。

本地同步云盘的两种情况

(1)网络连接建立的过程比本地扫描文件的过程耗时短
image.png
(2)网络连接建立的过程比本地扫描文件的过程耗时长
image.png

完整流程描述

(1)主线程通过开启异步线程方式处理建立云端网络连接的任务;
(2)主线程处理扫描本地文件任务;
(3)主线程需要等待异步线程处理的最终结果;
(4)主线程和异步线程的任务都执行完毕,等同于数据同步的准备工作执行完毕,开始进行同步上传;
(5)同步上传完毕,关闭网络连接

核心代码示例

(1)异步线程执行建立网络连接请求任务
image.png
(2)主线程处理本地文件扫描及阻塞等待异步线程的处理结果
image.png