线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。
在java中可以通过两种方式创建线程:
java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以你可以继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大,call()方法可以有返回值并且可以抛出异常。运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
// 继承Thread类创建线程
public class MyThread extends Thread{
public void run(){
System.out.println(" Thread Running " + Thread.currentThread().getName());
}
}
// 实现Runnable接口创建线程
public class MyRunnable implements Runnable{
public void run(){
System.out.println(" Create Thread " + Thread.currentThread().getName());
}
}
// 实现Callable接口创建线程
class MyCallable implements Callable {
public String call() throws Exception {
return Thread.currentThread().getName();
}
}
//starting Thread in Java
Thread mythread = new MyThread(); //Thread created not started
mythread.setName("T1");
mythread.start();
Thread myrunnable = new Thread(new MyRunnable(),"T2"); //Thread created
myrunnable.start();
MyCallable td = newMyCallable();
FutureTask result = new FutureTask<>(td);
Thread myCallable = new Thread(result);
myCallable.start();
参考:
https://javarevisited.blogspot.com/2011/02/how-to-implement-thread-in-java.html
https://www.cnblogs.com/3s540/p/7172146.html
以上成员均在 java.util.concurrent包中, 是 JDK并发包的核心类。其中ThreadpoolExecutor表示一个线程池。 Executors类则扮演着线程池工厂的角色,通过 Executors可以取得一个拥特定功能的线程池。从 UML图中亦可知, ThreadPoolExecutor类实现了 Executor接口, 因此通过这个接口, 任何 Runnable的对象都可以被 ThreadPoolExecutor线程池调度。
//五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue)
//六个参数的构造函数-1
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory)
//六个参数的构造函数-2
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
RejectedExecutionHandler handler)
//七个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
详细参数解释(可以对照上图线程图原理图):
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
1.SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大
2.LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize
3.ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误
4.DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
参考:
Java并发编程:线程池的使用:https://www.cnblogs.com/dolphin0520/p/3932921.html
线程池,这一篇或许就够了:https://www.jianshu.com/p/210eab345423
线程池的使用(线程池重点解析):https://www.cnblogs.com/zzuli/p/9386463.html
ForkJoinPool运用了Fork/Join原理,使用“分而治之”的思想,将大任务分拆成小任务分配给多个线程执行,最后合并得到最终结果,加快运算。
ForkJoinPool里有三个重要的角色:
ForkJoinWorkerThread(下文简称worker):包装Thread;
WorkQueue:任务队列,双向;
ForkJoinTask:worker执行的对象,实现了Future。两种类型,一种叫submission,另一种就叫task。
参考:
线程池篇(五):ForkJoinPool - 1:https://www.jianshu.com/p/de025df55363
线程池篇(五):ForkJoinPool - 2:https://www.jianshu.com/p/6a14d0b54b8d
如何使用 ForkJoinPool 以及原理:http://blog.dyngr.com/blog/2016/09/15/java-forkjoinpool-internals/
Java通过Executors提供五种线程池,分别为:
示例:
ExecutorService service = Executors.newFixedThreadPool(5);
Future future = service.submit(()->{
try{
Thread.sleep(5000);
}catch (Exception e){
e.printStackTrace();
}
return 1;
});
System.out.println(future.get().toString());
参考:
http://www.cnblogs.com/webglcn/p/5265901.html
Spring框架提供了线程池和定时任务执行的抽象接口:TaskExecutor和TaskScheduler来支持异步执行任务和定时执行任务功能。
Spring的TaskExecutor接口与java.util.concurrent.Executor接口相同。该接口具有单个方法(execute(Runnable task)),该方法根据线程池的语义和配置接受要执行的任务。
实现类名 | 对应解释 |
---|---|
SyncTaskExecutor | 该实现类不会执行异步调用。 相反,每次调用都在调用的线程中进行(翻译过来也即同步任务执行器)。 它主要用于不需要多线程的情况,例如在简单的测试用例中。 |
SimpleAsyncTaskExecutor | 此实现不会重用任何线程。 相反,它为每次调用启动一个新线程。 但是,它确实支持并发限制,该限制会阻止超出限制的任何调用,直到释放插槽为止。(说简单了,就是要使用了直接创建一个线程) |
ConcurrentTaskExecutor | 此实现是java.util.concurrent.Executor实例的适配器。很少需要直接使用ConcurrentTaskExecutor(官网自己都觉得很少使用,不过相对于ThreadPoolTaskExecutor,官网推荐如果ThreadPoolTaskExecutor不够灵活,无法满足需求,则可以使用ConcurrentTaskExecutor)。 |
ThreadPoolTaskExecutor | 杀手锏级的任务调度器(最常用),可以说已经足够满足我们的需求了(除非,非常非常特例才使用ConcurrentTaskExecutor)。官网翻译重要片段:公开了bean属性,用于配置java.util.concurrent.ThreadPoolExecutor并将其包装在TaskExecutor中 |
WorkManagerTaskExecutor | 此实现使用CommonJ WorkManager作为其后备服务提供程序,并且是在Spring应用程序上下文中在WebLogic或WebSphere上设置基于CommonJ的线程池集成的中心便利类。 |
DefaultManagedTaskExecutor | 此实现在JSR-236兼容的运行时环境(例如Java EE 7+应用程序服务器)中使用JNDI获取的ManagedExecutorService,为此目的替换CommonJ WorkManager。(说明了就是依赖环境) |
用于在将来的某个时间点调度任务。
TaskScheduler
接口是定时器的抽象,它的源代码如下。可以看到,该接口包含了一组方法用于指定任务执行的时间。
注:自带的Scheduled,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多,本文主要介绍。
public interface TaskScheduler {
//指定一个具体时间点执行定时任务,可以动态的指定时间,开启任务。只执行一次。
ScheduledFuture schedule(Runnable task, Trigger trigger);
ScheduledFuture schedule(Runnable task, Date startTime);
//指定时间开始执行,循环任务,指定一个间隔周期(毫秒计时)注:不管上一个周期是否执行完,到时间下个周期就开始执行
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
//立即执行,循环任务,指定一个执行周期(毫秒计时) 注:不管上一个周期是否执行完,到时间下个周期就开始执行
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
//指定时间开始执行,循环任务,指定一个间隔周期(毫秒计时)注:上一个周期执行完,等待delay时间,下个周期开始执行
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
//立即执行,循环任务,指定一个间隔周期(毫秒计时)注:上一个周期执行完,等待delay时间,下个周期开始执行
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}
实现类名 | 对应解释 |
---|---|
ConcurrentTaskScheduler | 该类实际继承了ConcurrentTaskExecutor对象。只是实现了TaskScheduler接口,增加了相关定时调度任务的方法。 |
ThreadPoolTaskScheduler | spring对该类设计原则同ThreadPoolTaskExecutor类。是为了定时调度任务不依赖相关的运行容器(例如weblogic、WebSphere等)。其底层委托给ScheduledExecutorService,向外暴露相关的常见bean配置属性。TaskScheduler接口的默认实现类,可以设置执行线程池数(默认一个线程)。 |
参考:
https://www.jianshu.com/p/ad7bf4472453
其实质还是内部封装ThreadPoolExecutor类。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean(name = "taskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(1 << 2);
// 设置最大线程数
executor.setMaxPoolSize(1 << 3);
// 设置队列容量
executor.setQueueCapacity(1 << 3);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置默认线程名称
executor.setThreadNamePrefix("task-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
使用示例:
@Service
public class TestThreadPool {
@Resource
private TaskExecutor taskExecutor;
public void test(){
taskExecutor.execute(() -> {
//todo something
});
//或
taskExecutor.execute(new Runnable() {
public void run() {
// todo something
}
});
}
}
这种scheduler机制是task的默认机制,而且从名字上也可以看到它是一种委托到ThreadPool的机制,每个 调度任务 都会分配到 线程池 中的一个 线程 去执行。也就是说,任务是 并发执行,互不影响的。可以将它看成一个 轻量级 的 Quartz,而且使用起来比 Quartz 简单许多,但是适用于 单节点的定时任务调度。
ThreadPoolTaskScheduler是TaskScheduler接口的默认实现类,多线程定时任务执行。可以设置执行线程池数(默认一个线程)。在spring-bean.xml中进行配置
使用task:scheduler标签时,spring会实例化一个ThreadPoolTaskScheduler。
@Lazy
@Service
public class DemoTask {
public void job1() {
System.out.println("Task job1: " + System.currentTimeMillis());
}
public void job2() {
System.out.println("Task job2: " + System.currentTimeMillis());
}
}
XML 的配置,定义一个 scheduler,然后定义具体任务 scheduled-task。
1、task:scheduler 定义一个 ThreadPoolTaskScheduler, 提供唯一参数指定了池大小。
2、task:scheduled-tasks :指定所采用的任务池
参数名 | 说明 |
---|---|
initial-delay | 任务初始延迟,单位 milliseconds |
fixed-delay | 任务固定间隔时间,时间自前一次完成后计算,单位 milliseconds |
fixed-rate | 任务固定间隔时间,时间自前一次开始后计算,单位 milliseconds |
cron | cron 表达式 |
trigger | 实现 Trigger 接口的 Bean 的引用 |
ref | 具体 Task 的方法所在类 |
method | 具体 Task 的方法名 |
使用@Scheduler
注解先要使用task:annotation-driven启动注解开关。
@Service
public class DemoTask {
@Scheduled(cron = "5 * * * * ?")
public void job1() {
System.out.println("Task job1: " + System.currentTimeMillis());
}
@Scheduled(initialDelay = 100, fixedDelay = 1000)
public void job2() {
System.out.println("Task job2: " + System.currentTimeMillis());
}
}
ListenableFuture是对原有Future的增强,可以用于监听Future任务的执行状况,是执行成功还是执行失败,并提供响应的接口用于对不同结果的处理。
public class ListenFutureTest {
public static void main(String[] args) {
testListenFuture();
}
public static void testListenFuture() {
System.out.println("主任务执行完,开始异步执行副任务1.....");
ListeningExecutorService pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
ListenableFuture future = pool.submit(new Task());
Futures.addCallback(future, new FutureCallback() {
@Override
public void onSuccess(String result) {
System.out.println("成功,结果是:" + result);
}
@Override
public void onFailure(Throwable t) {
System.out.println("出错,业务回滚或补偿");
}
});
System.out.println("副本任务启动,回归主任务线,主业务正常返回2.....");
}
}
class Task implements Callable {
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(1);
// int a =1/0;
return "task done";
}
}
通过Guava中Futures类中allAsList和successfulAsList阻塞获得所有线程的执行结果。
注:当其中一个Future发生异常时候,将程序报错。
ListeningExecutorService pool = MoreExecutors.newDirectExecutorService();
ListenableFuture future1 = pool.submit(() -> "Hello");
ListenableFuture future2 = pool.submit(() -> 2);
ListenableFuture> future = Futures.allAsList(future1, future2);
List
参考:
https://www.kancloud.cn/wizardforcel/guava-tutorial/106940
http://www.cnblogs.com/whitewolf/p/4113860.html
https://codeday.me/bug/20190121/556991.html
CompletableFuture类实现了CompletionStage和Future接口。
* @since 1.8
*/
public class CompletableFuture implements Future, CompletionStage {
Future是Java 5添加的类,用来描述一个异步计算的结果;CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段;
通过静态工厂方法创建:
方法名 | 描述 |
---|---|
runAsync(Runnable runnable) |
默认使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。 |
runAsync(Runnable runnable, Executor executor) |
使用指定的thread pool执行异步代码。 |
supplyAsync(Supplier supplier) |
使用ForkJoinPool.commonPool()作为它的线程池执行异步代码,异步操作有返回值 |
supplyAsync(Supplier supplier, Executor executor) |
使用指定的thread pool执行异步代码,异步操作有返回值 |
注:
(1)runAsync 和 supplyAsync 方法的区别是runAsync返回的CompletableFuture是没有返回值的。
(2)这些线程都是Daemon线程,主线程结束Daemon线程不结束,只有JVM关闭时,生命周期终止。
示例:
CompletableFuture completableFuture = CompletableFuture.supplyAsync(()->"hello world!");
String ret = completableFuture.get();
System.out.println(ret);
public CompletionStage thenApply(Function super T,? extends U> fn);
public CompletionStage thenApplyAsync(Function super T,? extends U> fn);
public CompletionStage thenApplyAsync(Function super T,? extends U> fn,Executor executor);
以Async结尾的方法都是可以异步执行的,如果指定了线程池,会在指定的线程池中执行,如果没有指定,默认会在ForkJoinPool.commonPool()中执行,有入参有返回值。
示例:
String result = CompletableFuture.supplyAsync(() -> "hello").thenApply(s -> s + " world").join();
System.out.println(result);
public CompletionStage thenAccept(Consumer super T> action);
public CompletionStage thenAcceptAsync(Consumer super T> action);
public CompletionStage thenAcceptAsync(Consumer super T> action,Executor executor);
thenAccept是针对结果进行消耗,因为他的入参是Consumer,有入参无返回值。
示例:
CompletableFuture.supplyAsync(() -> "hello").thenAccept(s -> System.out.println(s+" world"));
public CompletionStage thenRun(Runnable action);
public CompletionStage thenRunAsync(Runnable action);
public CompletionStage thenRunAsync(Runnable action,Executor executor);
thenRun它的入参是一个Runnable的实例,表示当上步执行完,不关心结果就执行下步操作。
示例:
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
}).thenRun(() -> System.out.println("hello world"));
public CompletionStage thenCombine(CompletionStage extends U> other,BiFunction super T,? super U,? extends V> fn);
public CompletionStage thenCombineAsync(CompletionStage extends U> other,BiFunction super T,? super U,? extends V> fn);
public CompletionStage thenCombineAsync(CompletionStage extends U> other,BiFunction super T,? super U,? extends V> fn,Executor executor);
需要原来的处理返回值,并且other代表的CompletionStage也要返回值之后,利用这两个返回值,进行转换后返回指定类型的值。
示例
String result = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "world";
}), (s1, s2) -> s1 + " " + s2).join();
System.out.println(result); //输出:hello world
public CompletionStage thenAcceptBoth(CompletionStage extends U> other,BiConsumer super T, ? super U> action);
public CompletionStage thenAcceptBothAsync(CompletionStage extends U> other,BiConsumer super T, ? super U> action);
public CompletionStage thenAcceptBothAsync(CompletionStage extends U> other,BiConsumer super T, ? super U> action, Executor executor);
它需要原来的处理返回值,并且other代表的CompletionStage也要返回值之后,利用这两个返回值,进行消耗。
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "world";
}), (s1, s2) -> System.out.println(s1 + " " + s2));
public CompletionStage runAfterBoth(CompletionStage> other,Runnable action);
public CompletionStage runAfterBothAsync(CompletionStage> other,Runnable action);
public CompletionStage runAfterBothAsync(CompletionStage> other,Runnable action,Executor executor);
不关心这两个CompletionStage的结果,只关心这两个CompletionStage执行完毕的动作,之后在进行操作(Runnable)。
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "s1";
}).runAfterBothAsync(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "s2";
}), () -> System.out.println("hello world"));
public CompletionStage exceptionally(Function fn);
该方法在运行时候不一定执行,只有在出现了异常,才exceptionally进行补偿。
示例:
String result = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (1 == 1) {
throw new RuntimeException("测试一下异常情况");
}
return "s1";
}).exceptionally(e -> {
System.out.println(e.getMessage());
return "hello world";
}).join();
System.out.println(result);
public CompletionStage whenComplete(BiConsumer super T, ? super Throwable> action);
public CompletionStage whenCompleteAsync(BiConsumer super T, ? super Throwable> action);
public CompletionStage whenCompleteAsync(BiConsumer super T, ? super Throwable> action,Executor executor);
Future的then/catchError/whenComplete
执行时序同Java的try/catch/finally
。 catchError
不一定执行,但whenComplete
肯定是最后执行。
String result = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (1 == 1) {
throw new RuntimeException("测试一下异常情况");
}
return "s1";
}).whenComplete((s, t) -> {
System.out.println(s);
System.out.println(t.getMessage());
}).exceptionally(e -> {
System.out.println(e.getMessage());
return "hello world";
}).join();
System.out.println(result);
public CompletionStage handle(BiFunction super T, Throwable, ? extends U> fn);
public CompletionStage handleAsync(BiFunction super T, Throwable, ? extends U> fn);
public CompletionStage handleAsync(BiFunction super T, Throwable, ? extends U> fn,Executor executor);
这里的完成时有两种情况,一种是正常执行,返回值。另外一种是遇到异常抛出造成程序的中断。
String result = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//出现异常
if (1 == 1) {
throw new RuntimeException("测试一下异常情况");
}
return "s1";
}).handle((s, t) -> {
if (t != null) {
return "hello world";
}
return s;
}).join();
System.out.println(result);
public T get()
public T get(long timeout, TimeUnit unit)
public T getNow(T valueIfAbsent)
public T join()
getNow
有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent
值。
join
返回计算的结果或者抛出一个unchecked异常(CompletionException)
public static CompletableFuture allOf(CompletableFuture>... cfs)
public static CompletableFuture anyOf(CompletableFuture>... cfs)
CompletableFuture completableFuture1=CompletableFuture.supplyAsync(()->{
//模拟执行耗时任务
System.out.println("task1 doing...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//返回结果
return "result1";
});
CompletableFuture completableFuture2=CompletableFuture.supplyAsync(()->{
//模拟执行耗时任务
System.out.println("task2 doing...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//返回结果
return "result2";
});
CompletableFuture anyResult=CompletableFuture.anyOf(completableFuture1,completableFuture2);
System.out.println("第一个完成的任务结果:"+anyResult.get());
CompletableFuture allResult=CompletableFuture.allOf(completableFuture1,completableFuture2);
//阻塞等待所有任务执行完成
allResult.join();
System.out.println("所有任务执行完成");
如果你用过Guava的Future类,你就会知道它的Futures辅助类提供了很多便利方法,用来处理多个Future,而不像Java的CompletableFuture,只提供了allOf、anyOf两个方法。 比如有这样一个需求,将多个CompletableFuture组合成一个CompletableFuture,这个组合后的CompletableFuture的计算结果是个List,它包含前面所有的CompletableFuture的计算结果,guava的Futures.allAsList可以实现这样的功能,但是对于java CompletableFuture,我们需要一些辅助方法:
public static CompletableFuture> sequence(List> futures) {
CompletableFuture allDoneFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
return allDoneFuture.thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
}
注:只有当每个操作很复杂需要花费相对很长的时间(比如,调用多个其它的系统的接口;比如,商品详情页面这种需要从多个系统中查数据显示的)的时候用CompletableFuture才合适,不然区别真的不大,还不如顺序同步执行。
参考:
https://www.jianshu.com/p/6f3ee90ab7d3
https://zhuanlan.zhihu.com/p/34921166
https://colobu.com/2016/02/29/Java-CompletableFuture/
http://www.cnblogs.com/cjsblog/p/9267163.html
https://www.baeldung.com/java-completablefuture
https://blog.csdn.net/u012129558/article/details/78962759