目录
1. 初始化线程的4种方式
1.1 继承 Thread类,重写run()方法
1.2 实现 Runnable 接口,重写run()方法
1.3 实现 Callable 接口 , FutureTask (可以拿到返回结果, 可以处理异常)
1.4 创建线程池直接提交任务(推荐),
1.5 四种创建线程方法的区别
2. 线程池详解
2.1 创建线程池方法1:执行器工具类创建线程池
2.1.1 执行器工具类 Executors创建线程池
2.1.2 线程池execute和submit区别(向线程池提交任务)
2.1.3 执行器工具类的4种线程池
2.2 创建线程池方法2(推荐):创建自定义线程池
2.2.1 线程池执行器ThreadPoolExecutor创建自定义线程池
2.2.2 构造方法的七个参数
2.2.3 线程池执行任务流程
2.3 使用线程池的好处
3. 异步编排 CompletableFuture
3.1 简介
3.2 创建异步对象,runAsync|supplyAsync
3.2.1 创建异步对象的方法
3.2.2 runAsync,不带线程返回值
3.2.3 supplyAsync 带线程返回值
3.3 线程结果感知和处理,使用whenCompleteAsync与exceptionally
3.3.1 简介
3.3.2 感知结果和异常但不处理,whenCompleteAsync
3.3.3 感知结果和异常并处理异常,whenCompleteAsync和exceptionally
3.4 线程结果感知和处理(推荐), handle 方法
3.5 线程串行化方法
3.5.0 简介
3.5.1 thenRunAsync
3.5.2 thenAcceptAsync
3.5.3 thenApplyAsync
3.6 两任务组合 - 都要完成
3.6.0 概述
3.6.1 runAfterBothAsync,不获取结果并处理新任务
3.6.2 thenAcceptBothAsync,获取结果并处理新任务
3.6.3 thenCombineAsync,,获取结果并获得新任务结果
3.7 两个任务 - 一个完成
3.7.0 概述
3.7.1 runAfterEitherAsync,不获取结果, 新任务无返回值。
3.7.2 acceptEitherAsync,获取结果, 新任务无返回值。
3.7.3 applyToEitherAsync,获取结果, 新任务有返回值。
3.8 多任务组合
3.8.0 概述
3.8.1 allOf,等待所有任务完成
3.8.2 anyOf,只要有一个任务完成
package site.xxx.gilimall.search.thread;
public class ThreadTest {
public static void main(String[] args) {
System.out.println("main......start...");
Thread01 thread01 = new Thread01();
thread01.start();
System.out.println("main......end...");
}
//继承 Thread类,重写run()方法
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getName());
Integer i=10/2;
System.out.println("运行结果:"+i);
}
}
}
运行结果
package site.xxx.gilimall.search.thread;
public class ThreadTest {
public static void main(String[] args) {
Thread02 thread02 = new Thread02();
new Thread(thread02).start();
System.out.println("main......end...");
}
//实现 Runnable 接口,重写run()方法
public static class Thread02 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getName());
Integer i=12/2;
System.out.println("运行结果:"+i);
}
}
}
package site.xxx.gilimall.search.thread;
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main......start...");
//可以拿到返回结果, 可以处理异常
FutureTask futureTask = new FutureTask<>(new Thread03());
new Thread(futureTask).start();
Integer i = (Integer) futureTask.get();
System.out.println("main......end..."+i);
}
//实现 Callable 接口
public static class Thread03 implements Callable {
@Override
public Object call() throws Exception {
System.out.println("当前线程:"+Thread.currentThread().getName());
Integer i=14/2;
System.out.println("运行结果:"+i);
return i;
}
}
}
注意
实现callable的方法可以拿到返回值
FutureTask继承了Runnable
我们以后再业务代码里面,前三种启动线程的方式都不用。将所有的多线程异步任务都交给线程池执行。
创建一个固定类型的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("当前线程"+Thread.currentThread());
}
});
区别:
总结:
1、实际开发中,只用线程池【高并发状态开启了n个线程,会耗尽资源】
2、当前系统中线程池只有一两个,每个异步任务提交给线程池让他自己去执行
Executors译为执行器、线程池。
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("当前线程"+Thread.currentThread());
}
});
execute:参数只能是Runnable,没有返回值
submit:参数可以是Runnable、Callable,返回值是FutureTask
注意:声明的线程池必须是全局的不然起不到效果
1、newCachedThreadPool:缓存线程池。核心线程数是0,如果空闲会回收所有线程
创建一个可缓存线程池, 如果线程池长度超过处理需要, 可灵活回收空闲线程, 若无可回收, 则新建线程。
2、newFixedThreadPool:固定大小的线程池。核心线程数 = 最大线程数,【不回收】
创建一个定长线程池, 可控制线程最大并发数, 超出的线程会在队列中等待。
3、newScheduledThreadPool:定时任务线程池。多久之后执行【可提交核心线程数,最大线程数是Integer.Max】
创建一个定长线程池, 支持定时及周期性任务执行。
4、newSingleThreadPool:单线程化的线程池。核心与最大都只有一个【不回收】,后台从队列中获取任务
创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, //核心线程数
200, //最大线程数量,控制资源并发
10, //存活时间
TimeUnit.SECONDS, //时间单位
new LinkedBlockingDeque<>( 100000), //任务队列,大小100000个
Executors.defaultThreadFactory(), //线程的创建工厂
new ThreadPoolExecutor.AbortPolicy()); //拒绝策略
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit,workQueue, threadFactory, handler);
1、先创建核心线程运行任务
2、核心线程满了,新任务放入阻塞队列
3、如果阻塞队列满了,继续创建线程,最多创建max个线程
4、如果max满了,拒绝策略会执行,否则抛出异常
5、线程执行完任务,队列里也没有新任务等待执行,该线程就会成为空闲线程,如果空闲时间超过存活时间,并且线程数超过核心线程数,该线程会被销毁。
练习:
一个线程池 core 7; max 20 , queue: 50, 100 并发进来怎么分配的;
先有 7 个能直接得到执行, 接下来 50 个进入队列排队, 在多开 13 个继续执行。 现在 70 个被安排上了。 剩下 30 个默认拒绝策略。
拒绝策略
1、丢弃最老的 Rejected
2、调用者同步调用,直接调用run方法,不创建线程了 Caller
3、直接丢弃新任务 Abort 【默认使用这个】
4、丢弃新任务,并且抛出异常 Discard
1、降低资源的消耗【减少创建销毁线程的开销】
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
2、提高响应速度【控制线程个数】
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
3、提高线程的可管理性【例如系统中可以创建两个线程池,核心线程池、非核心线程池【短信等】,关闭非核心线程池释放内存资源】
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
Completable译为“可完备化的” 。
CompletableFuture提供了非常强大的Future接口的扩展功能, 可以简化异步编程。
提供了函数式编程的能力, 可以通过回调的方式处理计算结果, 并且提供了转换和组合 CompletableFuture 的方法。CompletableFuture 类实现了 Future 接口, 所以你还是可以像以前一样通过get
方法阻塞或者轮询的方式获得结果, 但是这种方式不推荐使用。
CompletableFuture 和 FutureTask ( 构造参数为Callable实现类)同属于 Future 接口的实现类, 都可以获取线程的执行结果。
示例,使用异步可以缩短响应时间:
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。
// 1.获取sku的基本信息0.5s
// 2.获取sku的图片信息0.5s
// 3.获取sku的促销信息1s
// 4.获取spu的所有销售属性 1s
// 5.获取规格参数组及组下的规格参数1.5s
// 6.spu详情1s
假如商品详情页的每个查询,需要如下标注的时间才能完成那么,用户需要 6.5s 后才能看到商品详情页的内容。很显然是不能接受的。如果有多个线程同时完成这 6步操作,只需要 1.5s 即可完成响应。
CompletableFuture 提供了四个静态方法来创建一个异步操作:
public static CompletableFuture runAsync(Runnable runnable)
//无线程返回值,指定线程池
public static CompletableFuture runAsync(Runnable runnable,Executor executor)
public static CompletableFuture supplyAsync(Supplier supplier)
//有线程返回值,指定线程池
public static CompletableFuture supplyAsync(Supplier supplier,Executor executor)
1、 runAsync 都是没有线程返回结果的, supplyAsync 都是可以获取线程返回结果的
2、 可以传入自定义的线程池, 否则就用默认的线程池;
3、Async代表异步方法
public class ThreadTest {
// ExecutorService executorService = Executors.newFixedThreadPool(10);
public static ThreadPoolExecutor executor = new ThreadPoolExecutor( 5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>( 100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
CompletableFuture voidCompletableFuture = CompletableFuture.runAsync(() -> {
System.out.println("当前线程:"+Thread.currentThread().getName());
int i = 10 / 2;
System.out.println("运行结果...."+i);
}, executor);
}
}
public class ThreadTest {
// ExecutorService executorService = Executors.newFixedThreadPool(10);
public static ThreadPoolExecutor executor = new ThreadPoolExecutor( 5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>( 100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("运行结果...." + i);
return i; //有返回值
}, executor);
Integer integer = supplyAsync.get();
System.out.println("返回数据:"+integer);
}
}
public CompletableFuture whencomplete(BiConsumer super T,? super Throwable> action);
public CompletableFuture whenCompleteAsync(BiConsumer super T,? super Throwable> action);
public CompletableFuture whenCompleteAsync(BiConsumer super T,? super Throwable> action,Executor executor);
public CompletableFuture exceptionally(Function fn);
whenComplete 和 whenCompleteAsync 的区别:
whenComplete: 是执行当前任务的线程执行继续执行 whenComplete 的任务。
whenCompleteAsync: 是执行把 whenCompleteAsync 这个任务继续提交给线程池
来进行执行。
public class ThreadTest {
// ExecutorService executorService = Executors.newFixedThreadPool(10);
public static ThreadPoolExecutor executor = new ThreadPoolExecutor( 5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>( 100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("运行结果...." + i);
return i;
}, executor).whenCompleteAsync((res, exception) -> { //第一个参数是结果,第二个参数是异常
System.out.println("异步任务完成....感知到返回值为:"+res+"异常:"+exception);
},executor);
Integer integer = supplyAsync.get();
System.out.println("返回数据:"+integer);
}
}
模拟异常情况,将线程里计算改成“12/0”
public class ThreadTest { // ExecutorService executorService = Executors.newFixedThreadPool(10); public static ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 200, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>( 100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture
supplyAsync = CompletableFuture.supplyAsync(() -> { System.out.println("当前线程:" + Thread.currentThread().getName()); int i = 12 / 0; System.out.println("运行结果...." + i); return i; }, executor).whenCompleteAsync((res, exception) -> { System.out.println("异步任务完成....感知到返回值为:"+res+"异常:"+exception); },executor); Integer integer = supplyAsync.get(); System.out.println("返回数据:"+integer); } } 此处虽然得到了异常信息但是没有办法修改返回数据,使用
exceptionally
自定义异常时的返回值
异常情况处理
public class ThreadTest {
// ExecutorService executorService = Executors.newFixedThreadPool(10);
public static ThreadPoolExecutor executor = new ThreadPoolExecutor( 5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>( 100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 12 / 0;
System.out.println("运行结果...." + i);
return i;
//虽然能得到异常信息,但无法修改返回结果
}, executor).whenCompleteAsync((res, exception) -> {
System.out.println("异步任务完成....感知到返回值为:"+res+"异常:"+exception);
//可以感知异常,并返回自定义默认值
},executor).exceptionally(throwable -> {
return 0;
});
Integer integer = supplyAsync.get();
System.out.println("返回数据:"+integer);
}
}
无异常情况,正常返回不会进exceptionally,也就不会处理异常:
public class ThreadTest { // ExecutorService executorService = Executors.newFixedThreadPool(10); public static ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 200, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>( 100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture
supplyAsync = CompletableFuture.supplyAsync(() -> { System.out.println("当前线程:" + Thread.currentThread().getName()); int i = 12 / 2; System.out.println("运行结果...." + i); return i; }, executor).whenCompleteAsync((res, exception) -> { System.out.println("异步任务完成....感知到返回值为:"+res+"异常:"+exception); },executor).exceptionally(throwable -> { return 0; }); Integer integer = supplyAsync.get(); System.out.println("返回数据:"+integer); } }
和 complete 一样, 可对结果做最后的处理(可处理异常),可改变返回值。
总结:使用R apply(T t, U u); 可以感知异常,和修改返回值的功能。
有异常情况
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 12 / 0;
System.out.println("运行结果...." + i);
return i;
//处理方法执行结果
}, executor).handleAsync((res, throwable) -> {
if (res!=null){
return res*2;
}
if (throwable!=null){
System.out.println("出现异常"+throwable.getMessage());
return -1;
}
return 0;
},executor);
Integer integer = supplyAsync.get();
System.out.println("返回数据:"+integer);
}
无异常情况
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 12 / 6;
System.out.println("运行结果...." + i);
return i;
}, executor).handleAsync((res, throwable) -> {
if (res!=null){
return res*2;
}
if (throwable!=null){
System.out.println("出现异常"+throwable.getMessage());
return -1;
}
return 0;
},executor);
Integer integer = supplyAsync.get();
System.out.println("返回数据:"+integer);
}
总结:
一般用handle,因为whencomplete如果异常不能给定默认返回结果,需要再调用exceptionally,而handle可以
该方法作用:获得前一任务的返回值【自己也可以是异步执行的】,也可以处理上一任务的异常,调用exceptionally修改前一任务的返回值【例如异常情况时给一个默认返回值】而handle方法可以简化操作
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture runAsync= CompletableFuture.runAsync(() -> {
System.out.println("当前线程:"+Thread.currentThread().getName());
int i = 10 / 2;
System.out.println("运行结果...."+i);
}, executor).thenRunAsync(() -> {
System.out.println("任务二启动了...");
},executor);
System.out.println("返回数据:");
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture supplyAsync= CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:"+Thread.currentThread().getName());
int i = 10 / 2;
System.out.println("运行结果...."+i);
return i;
}, executor).thenAcceptAsync(res -> {
System.out.println("任务二启动了..."+"拿到了上一步的结果:"+res);
},executor);
System.out.println("返回数据:");
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 2;
System.out.println("运行结果...." + i);
return i;
}, executor).thenApplyAsync(res -> {
System.out.println("任务二启动了..." + "拿到了上一步的结果:" + res);
return res*2;
}, executor);
Integer integer = future.get();
System.out.println("返回数据:"+integer);
}
runAfterBoth:组合两个future,不需要获取之前任务future的结果,只需两个future处理完任务后,处理该任务。
thenAcceptBoth:组合两个future,获取前两个future任务的返回结果,然后处理任务,没有返回值。
thenCombine:组合两个future,获取前两个future的返回结果,并返回当前任务的返回值
public CompletableFuture thenCombine(CompletionStage extends U> other, BiFunction super T,? super U,? extends V> fn); public CompletableFuture thenCombineAsync(CompletionStage extends U> other, BiFunction super T,? super U,? extends V> fn); public CompletableFuture thenCombineAsync(CompletionStage extends U> other, BiFunction super T,? super U,? extends V> fn, Executor executor); public CompletableFuture thenAcceptBoth(CompletionStage extends U> other, BiConsumer super T, ? super U> action); public CompletableFuture thenAcceptBothAsync(CompletionStage extends U> other, BiConsumer super T, ? super U> action); public CompletableFuture thenAcceptBothAsync(CompletionStage extends U> other, BiConsumer super T, ? super U> action, Executor executor); public CompletableFuture runAfterBoth(CompletionStage> other, Runnable action); public CompletableFuture runAfterBothAsync(CompletionStage> other, Runnable action); public CompletableFuture runAfterBothAsync(CompletionStage> other, Runnable action, Executor executor);
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一线程开始:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("任务一运行结束...." + i);
return i;
}, executor);
CompletableFuture
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一线程开始:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("任务一运行结束...." + i);
return i;
}, executor);
CompletableFuture
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一线程开始:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("任务一运行结束...." + i);
return i;
}, executor);
CompletableFuture
runAfterEither: 两个任务有一个执行完成, 不需要获取 future 的结果, 处理任务, 也没有返回值。
acceptEither: 两个任务有一个执行完成, 获取它的返回值, 处理任务, 没有新的返回值。
applyToEither: 两个任务有一个执行完成, 获取它的返回值, 处理任务并有新的返回值。
public CompletableFuture applyToEither( CompletionStage extends T> other, Function super T, U> fn) { return orApplyStage(null, other, fn); } public CompletableFuture applyToEitherAsync( CompletionStage extends T> other, Function super T, U> fn) { return orApplyStage(asyncPool, other, fn); } public CompletableFuture applyToEitherAsync( CompletionStage extends T> other, Function super T, U> fn, Executor executor) { return orApplyStage(screenExecutor(executor), other, fn); } public CompletableFuture
acceptEither( CompletionStage extends T> other, Consumer super T> action) { return orAcceptStage(null, other, action); } public CompletableFuture acceptEitherAsync( CompletionStage extends T> other, Consumer super T> action) { return orAcceptStage(asyncPool, other, action); } public CompletableFuture acceptEitherAsync( CompletionStage extends T> other, Consumer super T> action, Executor executor) { return orAcceptStage(screenExecutor(executor), other, action); } public CompletableFuture runAfterEither(CompletionStage> other, Runnable action) { return orRunStage(null, other, action); } public CompletableFuture runAfterEitherAsync(CompletionStage> other, Runnable action) { return orRunStage(asyncPool, other, action); } public CompletableFuture runAfterEitherAsync(CompletionStage> other, Runnable action, Executor executor) { return orRunStage(screenExecutor(executor), other, action); }
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一线程开始:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("任务一运行结束...." + i);
return i;
}, executor);
CompletableFuture
测试发现,线程二睡了3秒钟,但是线程一完成了,达成runAfterEitherAsync执行条件,线程二就不继续执行了
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一线程开始:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("任务一运行结束...." + i);
return i;
}, executor);
CompletableFuture
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一线程开始:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("任务一运行结束...." + i);
return i;
}, executor);
CompletableFuture
//allOf: 等待所有任务完成
public static CompletableFuture allOf(CompletableFuture>... cfs) {
return andTree(cfs, 0, cfs.length - 1);
}
//anyOf: 只要有一个任务完成
public static CompletableFuture
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一线程开始:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("任务一运行结束...." + i);
return i;
}, executor);
CompletableFuture
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一线程开始:" + Thread.currentThread().getName());
int i = 12 / 2;
System.out.println("任务一运行结束...." + i);
return i;
}, executor);
CompletableFuture future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务二线程开始:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务二运行结束....");
return "hello";
}, executor);
CompletableFuture future03 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务三线程开始:" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务三运行结束....");
return "hello2";
}, executor);
CompletableFuture anyOf = CompletableFuture.anyOf(future01, future02, future03);
anyOf.get();//等待其中之一任务完成
System.out.println("返回数据:");
}