1、概述
JUC提供了一些工具类,实现了一些常见的线程间同步模式,常用同步器如下
- CountDownLatch : 计数递减门闩,用于控制一个线程等待其他线程都到达指定位置时才开始工作。
- CyclicBarrier : 循环栅栏,用于控制多个线程在一些指定的位置同步,可以指定多个位置。
- Phaser : 动态阶段同步器,相当于增强的CyclicBarrier和CountDownLatch。
- Semaphore : 信号量,用于控制线程同步。
- Exchanger : 交换器,用于两个线程交换数据。
- CompletableFuture : 多任务异步处理工具。
2、Semaphore 同步器
经典的信号量,通过计数器控制对共享资源的访问。
方法
- Semaphore(int count): 创建拥有 count 个许可证的信号量
- acquire()/acquire(int num) : 获取 1/num 个许可证
- release/release(int num) : 释放 1/num 个许可证
代码demo
package com.ybw.jdk8.demo.synchronizer;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Semaphore;
/**
* 同步器-信号量
*
* @author weixiansheng
* @version V1.0
* @className SemaphoreDemo
* @date 2023/11/15
**/
@Slf4j
public class SemaphoreDemo {
/**
* 这段代码演示了如何使用java.util.concurrent.Semaphore来控制同时访问某个资源的线程数量。
* 在这个例子中,Semaphore对象被初始化为具有2个许可证的数量。每个线程在执行任务之前都会获取许可证,
* 然后释放许可证以便其他线程可以获取。当所有许可证都被占用时,尝试获取许可证的线程将会被阻塞,直到有其他线程释放许可证。
* 这个例子中,两个线程每次运行1秒,因此每次只有一个线程运行,总共运行10秒。
*
* @param args
* @methodName: main
* @return: void
* @author: weixiansheng
* @date: 2023/11/15
**/
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(2);
Thread t1 = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
semaphore.acquire();
log.info("Thread 1: Running");
Thread.sleep(1000);
semaphore.release();
}
} catch (InterruptedException e) {
log.error("Thread 1: Interrupted");
}
});
Thread t2 = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
semaphore.acquire();
log.info("Thread 2: Running");
Thread.sleep(1000);
semaphore.release();
}
} catch (InterruptedException e) {
log.error("Thread 2: Interrupted");
}
});
t1.start();
t2.start();
}
}
这段代码演示了如何使用java.util.concurrent.Semaphore来控制同时访问某个资源的线程数量。在这个例子中,Semaphore对象被初始化为具有2个许可证的数量。每个线程在执行任务之前都会获取许可证,然后释放许可证以便其他线程可以获取。当所有许可证都被占用时,尝试获取许可证的线程将会被阻塞,直到有其他线程释放许可证。这个例子中,两个线程每次运行1秒,因此每次只有一个线程运行,总共运行10秒。
打印日志如下
[INFO ] 2023-11-15 16:53:54.887 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:54.887 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running
[INFO ] 2023-11-15 16:53:55.923 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running
[INFO ] 2023-11-15 16:53:55.923 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:56.933 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running
[INFO ] 2023-11-15 16:53:56.933 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:57.943 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running
[INFO ] 2023-11-15 16:53:57.943 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:58.957 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:58.957 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running
3、CountDownLatch 同步器
特征:
必须发生指定数量的事件后才可以继续运行 ( 比如赛跑比赛,裁判喊出 3,2,1 之后大家才同时跑 )
方法:
- CountDownLatch(int count): 必须发生 count 个数量才可以打开锁存器
- await: 等待锁存器,
- 如果想让某个线程处于等待中,该线程调用countdownLatch.await()。
- 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
- countDown: 触发事件
- 通过其他线程调用countdownLatch.countDown()减少计数器,直到减少到0,被await()挂起的线程恢复执行。
代码demo
package com.ybw.jdk8.demo.synchronizer;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
/**
* CountDownLatch 同步器
*
* @author weixiansheng
* @version V1.0
* @className CountDownLatchDemo
* @date 2023/11/15
**/
@Slf4j
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
log.info("Thread " + Thread.currentThread().getId() + " is running.");
log.info("Thread " + Thread.currentThread().getId() + " is finished.");
//递减锁存器的计数
latch.countDown();
}).start();
}
log.info("All threads have started.");
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
latch.await();
log.info("All threads have ended.");
}
}
日志如下
[INFO ] 2023-11-15 17:11:52.589 [main] c.y.j.d.s.CountDownLatchDemo - All threads have started.
[INFO ] 2023-11-15 17:11:52.589 [Thread-2] c.y.j.d.s.CountDownLatchDemo - Thread 24 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-3] c.y.j.d.s.CountDownLatchDemo - Thread 25 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-5] c.y.j.d.s.CountDownLatchDemo - Thread 27 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-8] c.y.j.d.s.CountDownLatchDemo - Thread 30 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-1] c.y.j.d.s.CountDownLatchDemo - Thread 23 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-7] c.y.j.d.s.CountDownLatchDemo - Thread 29 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-0] c.y.j.d.s.CountDownLatchDemo - Thread 22 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-4] c.y.j.d.s.CountDownLatchDemo - Thread 26 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-6] c.y.j.d.s.CountDownLatchDemo - Thread 28 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-9] c.y.j.d.s.CountDownLatchDemo - Thread 31 is running.
[INFO ] 2023-11-15 17:11:52.593 [Thread-3] c.y.j.d.s.CountDownLatchDemo - Thread 25 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-7] c.y.j.d.s.CountDownLatchDemo - Thread 29 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-2] c.y.j.d.s.CountDownLatchDemo - Thread 24 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-0] c.y.j.d.s.CountDownLatchDemo - Thread 22 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-1] c.y.j.d.s.CountDownLatchDemo - Thread 23 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-8] c.y.j.d.s.CountDownLatchDemo - Thread 30 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-6] c.y.j.d.s.CountDownLatchDemo - Thread 28 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-5] c.y.j.d.s.CountDownLatchDemo - Thread 27 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-4] c.y.j.d.s.CountDownLatchDemo - Thread 26 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-9] c.y.j.d.s.CountDownLatchDemo - Thread 31 is finished.
[INFO ] 2023-11-15 17:11:52.593 [main] c.y.j.d.s.CountDownLatchDemo - All threads have ended.
4、CyclicBarrier同步器
翻译为“可循环利用的屏障”。它是一个同步的工具, 能够允许一组线程去互相等待直到都到达了屏障,CyclicBarrier对于涉及到固定大小的线程是非常有用的,线程们必须相互等待。该屏障称之为循环屏障,是因为当等待屏障的线程被释放之后,该屏障能循环使用。
特征:
适用于只有多个线程都到达预定点时才可以继续执行 ( 比如斗地主,需要等齐三个人才开始 )。
方法:
- CyclicBarrier(int num) : 等待线程的数量
- CyclicBarrier(int num, Runnable action) : 等待线程的数量以及所有线程到达后的操作
- await() : 到达临界点后暂停线程
适用场景:
有若干个线程,比如说有N个线程,需要它们都到达了某一个点之后才能开始一起执行,也就是说假如其中只有N-1个线程到达了这个点,还差一个线程没到达,此时这N-1个线程都会进入等待状态,直到第N个线程也到达了这个点之后,这N个线程才开始一起进行执行状态,这个场景跟CountDownLatch很类似。
代码demo
package com.ybw.jdk8.demo.synchronizer;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
/**
* CyclicBarrier 同步器
*
* @author weixiansheng
* @version V1.0
* @className CyclicBarrierDemo
* @date 2023/11/15
**/
@Slf4j
public class CyclicBarrierDemo {
public static void main(String[] args) {
// 创建一个线程池
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(20);
int corePoolSize = 20;
int maximumPoolSize = 30;
long keepAliveTime = 60L;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
// 创建一个屏障
// parties:屏障解除之前必须调用await方法的线程数
// barrierAction:屏障解除时执行的动作
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
log.info(Thread.currentThread().getName() + "已经到达检查点");
});
for (int i = 0; i < 10; i++) {
threadPoolExecutor.submit(() -> {
try {
log.info(Thread.currentThread().getName() + "已经到达屏障");
cyclicBarrier.await();
log.info(Thread.currentThread().getName() + "已经冲破屏障");
} catch (Exception e) {
e.printStackTrace();
}
});
}
threadPoolExecutor.shutdown();
}
}
日志如下
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-5] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-5已经到达屏障
[INFO ] 2023-11-16 19:05:15.853 [pool-1-thread-4] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-4已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-7] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-7已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-10] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-10已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-3] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-3已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-9] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-9已经到达屏障
[INFO ] 2023-11-16 19:05:15.853 [pool-1-thread-1] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-1已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-6] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-6已经到达屏障
[INFO ] 2023-11-16 19:05:15.853 [pool-1-thread-2] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-2已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-8] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-8已经到达屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-2] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-2已经到达检查点
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-2] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-2已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-9] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-9已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-5] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-5已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-10] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-10已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-4] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-4已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-7] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-7已经冲破屏障
[INFO ] 2023-11-16 19:05:15.861 [pool-1-thread-6] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-6已经冲破屏障
[INFO ] 2023-11-16 19:05:15.861 [pool-1-thread-1] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-1已经冲破屏障
[INFO ] 2023-11-16 19:05:15.861 [pool-1-thread-3] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-3已经冲破屏障
[INFO ] 2023-11-16 19:05:15.861 [pool-1-thread-8] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-8已经冲破屏障
5、CompletableFuture
简单的任务,用Future获取结果还好,但我们并行提交的多个异步任务,往往并不是独立的,很多时候业务逻辑处理存在串行[依赖]、并行、聚合的关系。如果要我们手动用 Fueture 实现,是非常麻烦的。
CompletableFuture
是Future接口的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富地扩展,完美地弥补了Future上述的种种问题。
更为重要的是,CompletableFuture实现了对任务的编排能力。借助这项能力,我们可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。
CompletionStage
接口: 执行某一个阶段,可向下执行后续阶段。异步执行,默认线程池是ForkJoinPool.comcommonPool()。
方法思维导图
方法总结
- 描述依赖关系
- thenApply() 把前面异步任务的结果,交给后面的Function
- thenCompose()用来连接两个有依赖关系的任务,结果由第二个任务返回
- 描述and聚合关系
- thenCombine 任务合并,有返回值
- thenAccepetBoth 两个任务执行完成后,将结果交给thenAccepetBoth消耗,无返回值
- runAfterBoth 两个任务都执行完成后,执行下一步操作(Runnable)
- 描述or聚合关系
- applyToEither 两个任务谁执行的快,就使用那一个结果,有返回值
- acceptEither 两个任务谁执行的快,就消耗那一个结果,无返回值
- runAfterEither 任意一个任务执行完成,进行下一步操作(Runnable)
- 并行执行
- CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture并行执行。
静态方法
// 无返回值
public static CompletableFuture<Void> runAsync(Runnable runnable)
// 无返回值 可以自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
// 有返回值
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// 有返回值 可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
- supply开头:这种方法,可以返回异步线程执行之后的结果。
- run开头:这种不会返回结果,就只是执行线程任务。
如果你想异步运行一些后台任务并且不想从任务中返回任何东西,那么你可以使用run开头的。
示例代码
package com.ybw.jdk8.demo.synchronizer;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* @author weixiansheng
* @version V1.0
* @className CompletableFutureDemo
* @date 2023/11/16
**/
@Slf4j
public class CompletableFutureDemo {
public static void main(String[] args) throws InterruptedException {
//1、执行无返回结果的异步任务
CompletableFuture.runAsync(() -> log.info("执行无返回结果的异步任务"));
//2、执行有返回值的异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
log.info("执行有返回值的异步任务");
return "hello";
});
//3、获取异步任务的执行结果
// t和u,代表任务的执行结果和异常
future.whenComplete((t, u) -> {
// 任务执行完成后,执行回调
log.info("异步任务执行完成:{}", t);
});
TimeUnit.DAYS.sleep(1);
}
}
打印日志如下
[INFO ] 2023-11-16 19:44:57.515 [ForkJoinPool.commonPool-worker-9] c.y.j.d.s.CompletableFutureDemo - 执行无返回结果的异步任务
[INFO ] 2023-11-16 19:44:57.515 [ForkJoinPool.commonPool-worker-2] c.y.j.d.s.CompletableFutureDemo - 执行有返回值的异步任务
[INFO ] 2023-11-16 19:44:57.520 [ForkJoinPool.commonPool-worker-2] c.y.j.d.s.CompletableFutureDemo - 异步任务执行完成:hello
thenApply()方法
拿到上一个异步线程返回的结构进行后续处理。
示例代码
package com.ybw.jdk8.demo.synchronizer;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @author weixiansheng
* @version V1.0
* @className CompletableFutureDemo
* @date 2023/11/16
**/
@Slf4j
public class CompletableFutureDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
log.info("业务开始");
// 模拟业务时长
TimeUnit.SECONDS.sleep(1);
log.info("业务结束");
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "商品信息";
});
CompletableFuture<String> resultFuture = future.thenApply(name -> "订单信息 " + name);
log.info(resultFuture.get());
TimeUnit.DAYS.sleep(1);
}
}
打印日志
[INFO ] 2023-11-16 19:52:31.168 [ForkJoinPool.commonPool-worker-9] c.y.j.d.s.CompletableFutureDemo - 业务开始
[INFO ] 2023-11-16 19:52:32.183 [ForkJoinPool.commonPool-worker-9] c.y.j.d.s.CompletableFutureDemo - 业务结束
[INFO ] 2023-11-16 19:52:32.184 [main] c.y.j.d.s.CompletableFutureDemo - 订单信息 商品信息
thenAccept() 和 thenRun()方法
如果你不想从你的回调函数中返回任何东西,只想在 Future 完成后运行一些代码,那么可以使用thenAccept()
andthenRun()
方法。这些方法是消费者Consumer<? super T> action,通常用作回调链中的最后一个回调。
- thenAccept()可以拿到上一次的结果,然后执行。
- thenRun()无法拿到上一次的结果,thenRun()意为然后执行什么,里面接收一个Runnable参数。
- thenAccept、thenRun都是主线程执行的任务,顺序执行。
示例代码
package com.ybw.jdk8.demo.synchronizer;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @author weixiansheng
* @version V1.0
* @className CompletableFutureDemo
* @date 2023/11/16
**/
@Slf4j
public class CompletableFutureDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "商品信息");
future.thenAccept(product -> log.info("我拿到了 " + product));
future.thenRun(()->{
log.info("我执行完成了");
});
log.info("main");
TimeUnit.DAYS.sleep(1);
}
}
打印日志
[INFO ] 2023-11-16 20:01:28.468 [main] c.y.j.d.s.CompletableFutureDemo - 我拿到了 商品信息
[INFO ] 2023-11-16 20:01:28.471 [main] c.y.j.d.s.CompletableFutureDemo - 我执行完成了
[INFO ] 2023-11-16 20:01:28.471 [main] c.y.j.d.s.CompletableFutureDemo - main
complete()
注意complete()其实也是个消费操作,但是与thenRun()不同的是,里面可以可抛出的异常
// 区别就是不是异步处理
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)
示例代码
package com.ybw.jdk8.demo.synchronizer;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @author weixiansheng
* @version V1.0
* @className CompletableFutureDemo
* @date 2023/11/16
**/
@Slf4j
public class CompletableFutureDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 异步执行
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
int a = 10 / 0;
log.info("执行结束!");
return "hello";
});
// 完成时执行
future.whenComplete((t, action) -> log.info(t + " 执行完成!"));
// 异常时执行
future.exceptionally(t -> {
log.info("执行失败:" + t.getMessage());
return "异常";
}).join();
TimeUnit.DAYS.sleep(1);
}
}
日志打印
[INFO ] 2023-11-16 20:15:24.009 [main] c.y.j.d.s.CompletableFutureDemo - null 执行完成!
[INFO ] 2023-11-16 20:15:24.012 [main] c.y.j.d.s.CompletableFutureDemo - 执行失败:java.lang.ArithmeticException: / by zero
handle()
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)
handle方法集和上面的complete方法集没有区别,同样有两个参数一个返回结果和可抛出异常,区别就在于返回值。
CompletableFuture 组合
示例代码
package com.ybw.jdk8.demo.synchronizer;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @author weixiansheng
* @version V1.0
* @className CompletableFutureDemo
* @date 2023/11/16
**/
@Slf4j
public class CompletableFutureDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
String result = CompletableFuture.supplyAsync(() -> "商品信息")
.thenApply(product -> product + " 用户信息").join();
log.info(result);
TimeUnit.DAYS.sleep(1);
}
}
日志打印
[INFO ] 2023-11-16 20:21:46.542 [main] c.y.j.d.s.CompletableFutureDemo - 商品信息 用户信息
多个 CompletableFuture 组合在一起
假设有3个接口 获取用户信息,获取商品信息,获取会员信息,先每个接口1s,那个3个接口总耗时3s。按顺序执行此操作,但这将花费大量时间,所以异步线程会大大减少耗时,增加程序的性能。
CompletableFuture.allOf()
CompletableFuture.allOf()里面包含了多个CompletableFuture操作,接收一个CompletableFuture类型的可变参数。
package com.ybw.jdk8.demo.synchronizer;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @author weixiansheng
* @version V1.0
* @className CompletableFutureDemo
* @date 2023/11/16
**/
@Slf4j
public class CompletableFutureDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
log.info("任务开始");
// 异步执行1
CompletableFuture<String> future1 = CompletableFuture
.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("future1完成!");
return "future1完成!";
});
// 异步执行2
CompletableFuture<String> future2 = CompletableFuture
.supplyAsync(() -> {
log.info("future2完成!");
return "future2完成!";
});
// 等待两个任务都完成
CompletableFuture<Void> combindFuture = CompletableFuture
.allOf(future1, future2);
try {
// 等待所有任务都完成
combindFuture.get();
} catch (Exception e) {
log.error("异常", e);
}
log.info("任务结束");
TimeUnit.DAYS.sleep(1);
}
}
日志打印
[INFO ] 2023-11-17 09:49:58.601 [main] c.y.j.d.s.CompletableFutureDemo - 任务开始
[INFO ] 2023-11-17 09:49:58.639 [ForkJoinPool.commonPool-worker-2] c.y.j.d.s.CompletableFutureDemo - future2完成!
[INFO ] 2023-11-17 09:50:00.644 [ForkJoinPool.commonPool-worker-9] c.y.j.d.s.CompletableFutureDemo - future1完成!
[INFO ] 2023-11-17 09:50:00.644 [main] c.y.j.d.s.CompletableFutureDemo - 任务结束