异步 & 线程池
1.1 线程回顾
1.1.1 初始化线程的 4 种方式
1、继承 Thread
2、实现 Runnable
3、实现 Callable 接口 + FutureTask(可以拿到返回结果,可以处理异常)
4、线程池
方式一和方式二 主进程无法获取线程的运算结果
方式三:主进程可以获取当前线程的运算结果,但是不利于控制服务器种的线程资源,可以导致服务器资源耗尽
方式四:通过如下两种方式初始化线程池
(1) Executors:
-
newCachedThreadPool
-
创建一个可缓存的线程池,如果线程池长度超过需要,可灵活回收空闲线程,若无可回收,则新建线程
注: 任务过多,无限制的创建线程会造成内存溢出
-
-
newFixedThreadPool
- 创建一个指定长度的线程池,可控制线程最大并发数,超出的线程会再队列中等待
-
newScheduledThreadPool
- 创建一个定长线程池,支持定时及周期性任务执行
-
newSingleThreadExecutor
- 创建一个单线程化的线程池,她只会用唯一的工作线程来执行任务,保证所有任务
//创建固定数目线程的线程池。
public static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
//可缓存的无边界的
public static ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//定长线程池,支持定时及周期性任务执行
public static ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
// 单线程化的线程池
public static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
(2) 手动创建 并且制定参数
new ThreadPollExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit,unit,workQueue,threadFactory,handler);
通过线程池性能稳定,也可以获取执行结果,并捕获异常,但是,在业务复杂情况下,一个异步调用可能会依赖另一个异步调用的执行结果
1.1.2 线程池的 7 大参数
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造函数的参数含义如下:
corePoolSize: 指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去;
**maximumPoolSize: **指定了线程池中的最大线程数量,这个参数会根据你使用的**workQueue任务队列的类型,决定线程池会开辟的最大线程数量;
**keepAliveTime: **当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;
**unit:**keepAliveTime的单位
workQueue: 任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;
threadFactory: 线程工厂,用于创建线程,一般用默认即可;
handler: 拒绝策略;当任务太多来不及处理时,如何拒绝任务;
一般我们创建线程池时,为防止资源被耗尽,任务队列都会选择创建有界任务队列,但种模式下如果出现任务队列已满且线程池创建的线程数达到你设置的最大线程数时,这时就需要你指定ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略,来处理线程池"超载"的情况。ThreadPoolExecutor自带的拒绝策略如下:
1、AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作;
2、CallerRunsPolicy策略:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行;
3、DiscardOledestPolicy策略:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;
4、DiscardPolicy策略:该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;
以上内置的策略均实现了RejectedExecutionHandler接口,当然你也可以自己扩展RejectedExecutionHandler接口,定义自己的拒绝策略
运行流程:
1、如果线程池中的线程数量少于corePoolSize,即使线程池中有空闲线程,也会创建一个新的线程来执行新添加的任务;
2、如果线程池中的线程数量大于等于corePoolSize,但缓冲队列workQueue未满,则将新添加的任务放到workQueue中,按照FIFO的原则依次等待执行(线程池中有线程空闲出来后依次将缓冲队列中的任务交付给空闲的线程执行);
3、如果线程池中的线程数量大于等于corePoolSize,且缓冲队列workQueue已满,但线程池中的线程数量小于maximumPoolSize,则会创建新的线程来处理被添加的任务;
1.1.3开发中为什么使用线程池
- 降低资源的消耗
- 通过重复利用已创建好的线程降低线程的创建和销毁带来的损耗
- 提高响应速度
- 因为线程池中的线程没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
- 提高线程的客观理性
- 线程池会根据当前系统的特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销,无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
@Configuration
@Slf4j
public class ThreadPoolConfig implements AsyncConfigurer {
/**
* 声明线程池bean
*
* @return
*/
@Bean
public Executor taskExecutor() {
//此方法返回可用处理器的虚拟机的最大数量; 不小于1
int core = 5;
log.info("初始化线程池=====>核心线程数:"+core);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数:线程池创建时候初始化的线程数
executor.setCorePoolSize(core);
//最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(core*2+1);
//允许线程的空闲时间600秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(600);
//缓冲队列:用来缓冲执行任务的队列
//如果传入值大于0,底层队列使用的是LinkedBlockingQueue,否则默认使用SynchronousQueue
executor.setQueueCapacity(10);
// 设置默认线程名前缀 async-service-1
// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("pool-async-service-");
// 设置拒绝策略:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
/**
* @Description: 动态 构建线程池
* @Author: 徐刘
* @Date: 2021/6/11 11:03
* @param corePoolSize 核心线程数
* @param maxPoolSize 最大线程数
* @param keepAliveSeconds 空闲时间
* @param queueCapacity 队列容量大小
* @param threadNamePrefix 线程前缀
* @Return: java.util.concurrent.Executor
*/
public static ExecutorService buildThreadPoolExecutor(int corePoolSize,int maxPoolSize,int keepAliveSeconds,int queueCapacity,String threadNamePrefix) {
if (corePoolSize < 0 ||
maxPoolSize <= 0 ||
maxPoolSize < corePoolSize ||
keepAliveSeconds < 0||
queueCapacity<0){
throw new IllegalArgumentException();
}
//此方法返回可用处理器的虚拟机的最大数量; 不小于1
int core = Runtime.getRuntime().availableProcessors();
/**
* LinkedBlockingQueue有默认的容量大小为:Integer.MAX_VALUE
* LinkedBlockingQueue中的锁是分离的,生产者的锁PutLock,消费者的锁takeLock
* 而ArrayBlockingQueue生产者和消费者使用的是同一把锁;
*
* */
LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(queueCapacity);
// 设置拒绝策略:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS,
workQueue, new NamedThreadFactory(threadNamePrefix), new ThreadPoolExecutor.CallerRunsPolicy());
// 预启动所有核心线程
executor.prestartAllCoreThreads();
return executor;
}
/**
* @Description: 实现ThreadFactory工厂(参考Executors 里面DefaultThreadFactory 实现方法方法 修改线程名称前缀)
* 实现自定义的 线程前缀的命名
* @Author: 徐刘
* @Date: 2021/6/11 11:46
* @param
* @Return:
*/
static class NamedThreadFactory implements ThreadFactory{
private final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
public final String namePrefix;
/**
* @Description: 有参构造 支持自定义线程名
* @Author: 徐刘
* @Date: 2021/6/11 16:14
* @param threadNamePrefix 线程名称 前缀
* @Return:
*/
NamedThreadFactory(String threadNamePrefix){
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
if (StringUtils.isBlank(threadNamePrefix)){
namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
}else {
namePrefix = threadNamePrefix;
}
}
/**
* @Description: 无参构造 默认线程名前缀
* @Author: 徐刘
* @Date: 2021/6/11 16:14
* @param
* @Return:
*/
NamedThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
/**
* @Description: 配置异常处理机制
* @Author: 徐刘
* @Date: 2020/12/22 16:06
* @param
* @Return: org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex,method,params)->{
log.error("异步线程执行失败,执行方法:[{}],异常信息[{}],参数[{}] : ", method, ex.getMessage(),params);
};
}
}
示例:
1.用固定数线程模拟100个异步任务(五个线程 相当于一秒处理五个并发)
2.用配置好的线程池
@Qualifier("taskExecutor")
@Autowired
private Executor executor;
// 创建 固定大小的线程池
static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
//todo 1 :用固定数线程模拟100个异步任务(五个线程 相当于一秒处理五个并发)
// 任务阻塞结束 耗时 : 20017 毫秒!
@Test
public void test01() throws Exception {
Instant start = Instant.now();
List<CompletableFuture<Integer>> futureList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
int finalI = i;
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
//睡眠 1 秒
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("任务"+finalI+":supplyAsync==>当前线程:" + Thread.currentThread().getName());
return finalI;
},fixedThreadPool);
futureList.add(future);
}
CompletableFuture<Void> allOf = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()]));
System.out.println("最终的返回结果:"+ allOf.get());
System.out.println("任务阻塞结束 耗时 : "+ ChronoUnit.MILLIS.between(start, Instant.now())+" 毫秒!");
}
控制台输出结果:
1:固定线程
任务阻塞结束 耗时 : 20017 毫秒!
2:线程池:
任务阻塞结束 耗时 : 9006 毫秒!
1.2 CompletableFuture 异步编排
1.2.1 创建异步对象
CompletableFuture 提供了四个静态方法来创建一个异步操作
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor);
public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
1、runXxx 都是没有返回结果的,supplyXxxx都是可以获取返回结果的
2、可以传入自定义的线程池,否则就是用默认的线程池
3、根据方法的返回类型来判断是否该方法是否有返回类型
static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant start = Instant.now();
System.out.println("main....start.....");
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 2;
System.out.println("运行结果:" + i);
}, executor);
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 2;
System.out.println("运行结果:" + i);
return i;
}, executor);
Integer integer = future.get();
System.out.println("main....stop....." + integer);
System.out.println("任务阻塞结束 耗时 = "+ ChronoUnit.MILLIS.between(start, Instant.now()));
}
1.2.2 计算完成时回调方法
whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况
whenComplete 和 whenCompleteAsync 的区别
whenComplete :是执行当前任务的线程继续执行 whencomplete 的任务
whenCompleteAsync: 是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) ;
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) ;
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)
//捕获异常
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) ;
static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant start = Instant.now();
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 0;
System.out.println("运行结果:" + i);
return i;
}, executor).whenComplete((res,exception) ->{
// 虽然能得到异常信息,但是没法修改返回的数据
System.out.println("异步任务成功完成了...结果是:" +res + " ,异常是:" + exception);
}).exceptionally(throwable -> {
// 可以感知到异常,同时返回默认值
System.out.println("异常:"+throwable);
return 10;
});
Integer integer = future.get();
System.out.println("最终的结果:"+integer);
System.out.println("任务阻塞结束 耗时 : "+ ChronoUnit.MILLIS.between(start, Instant.now())+" 毫秒!");
}
1.2.3 handle 方法
和 complete 一样,可以对结果做最后的处理(可处理异常),可改变返回值
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) ;
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor);
static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant start = Instant.now();
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 2;
System.out.println("运行结果:" + i);
return i;
}, executor).handle((res,thr) ->{
if (res != null ) {
return res * 2;
}
if (thr != null) {
return 0;
}
return 0;
});
System.out.println("最终的结果:"+future.get());
System.out.println("任务阻塞结束 耗时 : "+ ChronoUnit.MILLIS.between(start, Instant.now())+" 毫秒!");
}
1.2.4 线程串行方法
//接收上一步的返回结果 并且有返回值
<U> ConnectionFuture<U> thenApply(Function<? super T, ? extends U> var1);
<U> ConnectionFuture<U> thenApplyAsync(Function<? super T, ? extends U> var1);
<U> ConnectionFuture<U> thenApplyAsync(Function<? super T, ? extends U> var1, Executor var2);
//接受上一步的返回结果 无返回值
ConnectionFuture<Void> thenAccept(Consumer<? super T> var1);
ConnectionFuture<Void> thenAcceptAsync(Consumer<? super T> var1);
ConnectionFuture<Void> thenAcceptAsync(Consumer<? super T> var1, Executor var2);
//不接收上一步的返回结果 无返回值
ConnectionFuture<Void> thenRun(Runnable var1);
ConnectionFuture<Void> thenRunAsync(Runnable var1);
ConnectionFuture<Void> thenRunAsync(Runnable var1, Executor var2);
thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任物的返回值
thenAccept方法:消费处理结果,接受任务处理结果,并消费处理,无返回结果
thenRun 方法:只要上面任务执行完成,就开始执行 thenRun ,只是处理完任务后,执行 thenRun的后续操作
带有 Async 默认是异步执行的,同之前,
以上都要前置任务完成
static ExecutorService executor = Executors.newFixedThreadPool(5);
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant start = Instant.now();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1:supplyAsync==>当前线程:" + Thread.currentThread().getName());
String str = "Hello";
return str;
}, executor).thenApplyAsync(res -> {
System.out.println("任务2:thenApplyAsync==>当前线程:" + Thread.currentThread().getName());
return res+" world";
}, executor).thenApplyAsync(res->{
// try {
// TimeUnit.SECONDS.sleep(3);
// } catch (Exception e) {
// e.printStackTrace();
// }
System.out.println("任务3:thenApplyAsync==>当前线程:" + Thread.currentThread().getName());
return res.toUpperCase();
},executor).whenComplete((res,ex)->{
System.out.println("任务4:whenComplete==>当前线程:" + Thread.currentThread().getName());
System.out.println("whenComplete 查看任务执行结果 不对结果造成改变:"+res);
});
String res = future.get();
System.out.println("最终的返回结果:"+res);
System.out.println("任务阻塞结束 耗时 : "+ ChronoUnit.MILLIS.between(start, Instant.now())+" 毫秒!");
}
1.2.5 两任务组合 - 都要完成
// 组合两个 future,获取两个 future的返回结果,并返回当前任务的返回值
<U, V> ConnectionFuture<V> thenCombine(CompletionStage<? extends U> var1, BiFunction<? super T, ? super U, ? extends V> var2);
<U, V> ConnectionFuture<V> thenCombineAsync(CompletionStage<? extends U> var1, BiFunction<? super T, ? super U, ? extends V> var2);
<U, V> ConnectionFuture<V> thenCombineAsync(CompletionStage<? extends U> var1, BiFunction<? super T, ? super U, ? extends V> var2, Executor var3);
//组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值
<U> ConnectionFuture<Void> thenAcceptBoth(CompletionStage<? extends U> var1, BiConsumer<? super T, ? super U> var2);
<U> ConnectionFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> var1, BiConsumer<? super T, ? super U> var2);
<U> ConnectionFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> var1, BiConsumer<? super T, ? super U> var2, Executor var3);
// 组合 两个 future,不需要获取 future 的结果,只需要两个 future处理完成任务后,处理该任务,
ConnectionFuture<Void> runAfterBoth(CompletionStage<?> var1, Runnable var2);
ConnectionFuture<Void> runAfterBothAsync(CompletionStage<?> var1, Runnable var2);
ConnectionFuture<Void> runAfterBothAsync(CompletionStage<?> var1, Runnable var2, Executor var3);
两个任务必须都完成,触发该任务
thenCombine: 组合两个 future,获取两个 future的返回结果,并返回当前任务的返回值
thenAccpetBoth: 组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值
runAfterBoth:组合 两个 future,不需要获取 future 的结果,只需要两个 future处理完成任务后,处理该任务,
static ExecutorService executor = Executors.newFixedThreadPool(5);
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant start = Instant.now();
//任务一
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1:supplyAsync==>当前线程:" + Thread.currentThread().getName());
String str = "Hello";
return str;
}, executor);
String res1 = future1.get();
// 任务二
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("任务2:supplyAsync==>当前线程:" + Thread.currentThread().getName());
return " world";
},executor);
//todo 1:获取两个 future的返回结果,并返回当前任务的返回值
// CompletableFuture<String> all = future1.thenCombine(future2, (f1, f2) -> {
// System.out.println("任务3:thenCombine==>当前线程:" + Thread.currentThread().getName());
// return f1 + f2;
// });
//todo 2: 获取两个 future 任务的返回结果,然后处理任务,没有返回值
// CompletableFuture<Void> all = future1.thenAcceptBoth(future2, (f1, f2) -> {
// System.out.println("任务3:thenAcceptBoth==>当前线程:" + Thread.currentThread().getName());
// System.out.println("任务3结果:" + f1 + f2);
// });
//todo 3: 两个 future,不需要获取 future 的结果,只需要两个 future处理完成任务后,处理该任务,
CompletableFuture<Void> all = future1.runAfterBoth(future2, new Runnable() {
@Override
public void run() {
System.out.println("任务3:runAfterBoth==>当前线程:" + Thread.currentThread().getName());
System.out.println("任务3 :runAfterBoth 无入参 无出参!");
}
});
System.out.println("最终的返回结果:"+ all.get());
System.out.println("任务阻塞结束 耗时 : "+ ChronoUnit.MILLIS.between(start, Instant.now())+" 毫秒!");
}
1.2.6 两任务组合 - 一个完成
// 两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值
<U> ConnectionFuture<U> applyToEither(CompletionStage<? extends T> var1, Function<? super T, U> var2);
<U> ConnectionFuture<U> applyToEitherAsync(CompletionStage<? extends T> var1, Function<? super T, U> var2);
<U> ConnectionFuture<U> applyToEitherAsync(CompletionStage<? extends T> var1, Function<? super T, U> var2, Executor var3);
// 两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值
ConnectionFuture<Void> acceptEither(CompletionStage<? extends T> var1, Consumer<? super T> var2);
ConnectionFuture<Void> acceptEitherAsync(CompletionStage<? extends T> var1, Consumer<? super T> var2);
ConnectionFuture<Void> acceptEitherAsync(CompletionStage<? extends T> var1, Consumer<? super T> var2, Executor var3);
// 两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返回值
ConnectionFuture<Void> runAfterEither(CompletionStage<?> var1, Runnable var2);
ConnectionFuture<Void> runAfterEitherAsync(CompletionStage<?> var1, Runnable var2);
ConnectionFuture<Void> runAfterEitherAsync(CompletionStage<?> var1, Runnable var2, Executor var3);
当两个任务中,任意一个future 任务完成时,执行任务
applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值
acceptEither: 两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值
runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返回值
static ExecutorService executor = Executors.newFixedThreadPool(5);
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant start = Instant.now();
//任务一
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("任务1:supplyAsync==>当前线程:" + Thread.currentThread().getName());
String str = "Hello";
return str;
}, executor);
// 任务二
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("任务2:supplyAsync==>当前线程:" + Thread.currentThread().getName());
return " world";
},executor);
//todo 1:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值
// CompletableFuture<String> all = future1.applyToEither(future2, (res) -> {
// System.out.println("任务3:applyToEither==>当前线程:" + Thread.currentThread().getName());
// switch (res){
// case "Hello" :
// return "任务三最先获取到的是任务一的结果:"+res;
// case " world":
// return "任务三最先获取到的是任务2的结果:"+res;
// }
// return "都没匹配上:"+res;
// });
//todo 2: 两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值
// CompletableFuture<Void> all = future1.acceptEither(future2, res -> {
// System.out.println("任务3:thenAccept==>当前线程:" + Thread.currentThread().getName());
// switch (res){
// case "Hello" :
// System.out.println("任务3最先获取到的是任务1的结果:"+res);
// break;
// case " world":
// System.out.println("任务3最先获取到的是任务2的结果:"+res);
// break;
// default:
// System.out.println("都没匹配上:"+res);
// }
// });
//todo 3: 两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返回值
CompletableFuture<Void> all = future1.runAfterEither(future2, new Runnable() {
@Override
public void run() {
System.out.println("任务3:runAfterEither==>当前线程:" + Thread.currentThread().getName());
System.out.println("任务3 :runAfterEither 无入参 无出参!");
}
});
System.out.println("最终的返回结果:"+ all.get());
System.out.println("任务阻塞结束 耗时 : "+ ChronoUnit.MILLIS.between(start, Instant.now())+" 毫秒!");
}
1.2.7 多任务组合
allOf:等待所有任务完成
anyOf:只要有一个任务完成
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);
static ExecutorService executor = Executors.newFixedThreadPool(5);
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant start = Instant.now();
//任务一
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
//睡眠 3 秒
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("任务1:supplyAsync==>当前线程:" + Thread.currentThread().getName());
String str = "Hello";
return str;
}, executor);
// 任务二
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
// 睡眠 2 秒
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("任务2:supplyAsync==>当前线程:" + Thread.currentThread().getName());
return " world";
},executor);
//todo 1:allof 所有都完成
// CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2).whenComplete((res, exc) -> {
// System.out.println("任务3:allOf==>当前线程:" + Thread.currentThread().getName());
// System.out.println("任务3:最终处理结果" + res + " 异常信息:" + exc);
// });
//todo 2: anyOf 任意一个完成
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2).whenComplete((res, exc) -> {
System.out.println("任务3:anyOf==>当前线程:" + Thread.currentThread().getName());
System.out.println("任务3:最终处理结果" + res + " 异常信息:" + exc);
});
System.out.println("最终的返回结果:"+ anyOf.get());
System.out.println("任务阻塞结束 耗时 : "+ ChronoUnit.MILLIS.between(start, Instant.now())+" 毫秒!");
}