什么是Promise模式
一个完整流程的实现,需要执行两个任务,任务A和任务B。
如果任务A(烧水任务)和任务B(准备茶杯,茶叶)需同步执行,即先执行完任务A再执行完任务B,则执行的时间就是任务A+任务B的执行时间。
基于Promise模式,可以在执行任务B(准备茶叶,茶杯任务)的同时,可以用Promise实现异步执行任务A(烧水任务),即可以在执行任务A的同时执行任务B。然后,在任务A的主线上,如果任务A先于任务B执行结束,并不会立即停止主线流程,而是会阻塞等待任务B的执行结果,等到主线也得到任务B的结果后,这条主线才算执行完成。
Promise模式介绍
主要由4部分组成,1:Promisor;2:Executor;3:Promise;4:Result
(1)Executor需要在Promisor的create()方法中执行;
(2)create()的返回值是Promise;
(3)Result则是在Promise中的get()方法的返回值。
Promisor类
Promisor类会对外暴露一个create方法,一旦执行这个create方法,就会发生两件事:
(1)执行器Executor开始异步执行任务;
(2)Promisor会创建并返回一个Promise;
Executor执行器
在Promisor类中定义一个任务,Executor会异步执行这个任务。
Promise对象
是Promisor的create()方法的返回值,是立即返回。(类似买彩票,返回值就是买彩票拿到的号码。并不能立即知道结果,但也不需要在彩票站等待结果,可以直接拿着号码回去。等到开奖的时间到了,再根据这个号码查询是否中奖。)
isDone()方法:用来表示任务是在执行中还是任务已经完成,但不会告知任务的结果。
get()方法:获取任务执行的结果,在获取结果的时候,需要进行阻塞等待。
没有Promise模式下处理多任务
没有Promise模式下同步处理多任务
任务执行过程为串行化,必须等待前一个线程执行完,再去执行后一个线程。任务执行的总耗时就是两个线程的执行时间的总和。
没有Promise模式下异步处理多任务
可能会存在一种现象,异步线程的执行还未完成时,主线程的任务已经完成,就导致主线程执行完毕关闭任务流程,并未等到异步线程任务执行完毕。
基于Promise模式异步处理多任务
代码示例
package com.cmic.test.promise;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class Promisor {
public static Future<Object> create(long startTime) {
FutureTask<Object> futureTask = new FutureTask<>(() -> {
System.out.println("开始烧水,当前用时:" + (System.currentTimeMillis() - startTime) + "ms");
BoilWater boilWater = new BoilWater();
Thread.sleep(15000);
boilWater.setStatus(true);
return boilWater;
});
new Thread(futureTask).start();
return futureTask;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
long startTime = System.currentTimeMillis();
// part1: 使用promise模式异步处理烧水流程
Future<Object> promise = Promisor.create(startTime);
// part2:主线程处理准备茶叶茶杯的流程
System.out.println("开始准备茶叶茶杯,需要3分钟,当前用时:" + (System.currentTimeMillis() - startTime) + "ms");
TeaAndCup teaAndCup = new TeaAndCup();
Thread.sleep(3000);
teaAndCup.setStatus(true);
System.out.println("茶叶茶杯准备完成,当前用时:" + (System.currentTimeMillis() - startTime) + "ms");
// part3:通过promise来获取执行结果
// isDone: 查看任务是否执行完毕
if (!promise.isDone()) {
System.out.println("茶叶茶杯结束,等待烧水完成!");
}
// 获取promise模式下异步处理的结果,在未获得结果时, get() 会一直阻塞
BoilWater boilWater = (BoilWater)promise.get();
System.out.println("获取到烧水完成信号,当前用时:" + (System.currentTimeMillis() - startTime) + "ms");
System.out.println("开始泡茶");
System.out.println("总共耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}
}
输出结果
开始准备茶叶茶杯,需要3分钟,当前用时:107ms
开始烧水,当前用时:107ms
茶叶茶杯准备完成,当前用时:3118ms
茶叶茶杯结束,等待烧水完成!
获取到烧水完成信号,当前用时:15119ms
开始泡茶
总共耗时:15120ms
基于Promise实现的云盘同步优化
场景描述
本地 和网盘之间的数据同步,需要有两个任务执行完成才可以做数据同步。一是扫描本地文件内容任务,二是本地与网盘之间建立网络连接。只有执行完这两个任务,才可以进行数据同步的操作。
如果网络连接和扫描本地文件两个任务进行串行化执行的话,整个流程就会太慢。
场景要点明细
(1)用户并不知道有”建立云端网络连接”的过程;
(2)”建立云端网络连接”可能很耗时;
(3)”建立云端网络连接”并不需要在”扫描本地文件”之前先完成;
(4)在数据同步准备工作结束之前,必须得到”建立云端网络连接”的完成反馈。
(5)用户并不关注”建立云端网络连接”这个过程,所以可以将这个过程进行异步处理。但是最终的流程需要等待云端网络连接的建立结果,只有网络建立连接成功,才能算作准备完毕,所以需要在获得结果时主流程进行阻塞等待。
本地同步云盘的两种情况
(1)网络连接建立的过程比本地扫描文件的过程耗时短
(2)网络连接建立的过程比本地扫描文件的过程耗时长
完整流程描述
(1)主线程通过开启异步线程方式处理建立云端网络连接的任务;
(2)主线程处理扫描本地文件任务;
(3)主线程需要等待异步线程处理的最终结果;
(4)主线程和异步线程的任务都执行完毕,等同于数据同步的准备工作执行完毕,开始进行同步上传;
(5)同步上传完毕,关闭网络连接
核心代码示例
(1)异步线程执行建立网络连接请求任务
(2)主线程处理本地文件扫描及阻塞等待异步线程的处理结果