CompletableFuture 是什么
CompletableFuture 是 Java 8 中引入的一个强大的异步编程工具。它是 Java 的 Future 接口的一个实现,提供了对异步计算的更多控制和更丰富的功能。CompletableFuture 的主要特点包括:
异步执行: CompletableFuture 可以异步执行任务,并在任务完成后通知调用者。这使得应用程序可以更好地利用多线程并发处理。
链式操作: CompletableFuture 提供了大量的方法来组合和转换异步任务,允许开发者以链式的方式编写复杂的异步流程。
错误处理: CompletableFuture 提供了错误处理的方法,可以在任务执行过程中捕获和处理异常。
时间控制: CompletableFuture (java9以及9以上)提供了超时和延时等方法,可以更好地控制异步任务的执行时间。
可组合性: CompletableFuture 可以与其他 Java 8 特性如 Stream 和 CompletionStage
等结合使用,提高代码的可读性和可维护性。使用 CompletableFuture
可以帮助开发者编写更加简洁、易读和可维护的异步代码。它在处理许多并发任务、微服务调用、事件驱动架构等场景中都有广泛应用。
上面主要是CompletableFuture 的简单介绍今天我们主要针对他的超时场景做的分析以及不同版本实现的方案。
在我们业务场景中会使用CompletableFuture 提高并发处理但是我们假如有A和B 两个并发事件 A中出现死锁或者调用第三方接口超时、或者有其他导致线程一直无法释放的情况 这个时候调用CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()])).join();
会一直等待 特别我们高并发 线程直接拉满,影响整个系统稳定性。因此我们有必要做超时处理。
Java8 CompletableFuture 自定义实现超时方案
public class CompletableFutureTimeoutUtil {
/**
* 延迟类
*/
static final class Delayer {
static final class CompletableFutureDelaySchedulerFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("CompletableFutureDelaySchedulerFactory");
return t;
}
}
// 任务执行器
static final ScheduledThreadPoolExecutor delayer;
static {
(delayer = new ScheduledThreadPoolExecutor(
1, new CompletableFutureTimeoutUtil.Delayer.CompletableFutureDelaySchedulerFactory())).
setRemoveOnCancelPolicy(true);
}
}
/**
* 通过ScheduledThreadPoolExecutor schedule定的延迟时间之后执行一次性任务
*
* @param timeout
* @param unit
* @param <T>
* @return
*/
public static <T> CompletableFuture<T> timeoutAfter(long timeout, TimeUnit unit) {
CompletableFuture<T> result = new CompletableFuture<T>();
CompletableFutureTimeoutUtil.Delayer.delayer.schedule(() -> result.completeExceptionally(new TimeoutException()), timeout, unit);
return result;
}
/**
* 使用 applyToEither 方法,将 future 和 timeoutFuture 两个 CompletableFuture 合并,谁先完成就使用谁的结果
*
* @param t 异常发生后返回默认值
* @param future 参与执行的业务的任务
* @param timeout 超时时间
* @param unit 超时单位
* @param runnable 函数接口 可以保存执行保存日志发送Mq 消息
* @param <T>
* @return
*/
public static <T> CompletableFuture<T> completeOnTimeout(T t, CompletableFuture<T> future, long timeout, TimeUnit unit, Runnable runnable) {
final CompletableFuture<T> timeoutFuture = timeoutAfter(timeout, unit);
return future.applyToEither(timeoutFuture, Function.identity()).exceptionally((throwable) -> {
Optional.ofNullable(runnable).ifPresent(Runnable::run);
return t;
});
}
}
大概思想就是通过ScheduledThreadPoolExecutor 线程延迟执行任务 实现和业务线程的对比applyToEither() 方法接收两个 CompletableFuture 作为参数,并将它们并行执行。当其中一个 CompletableFuture 先完成时,就会触发另一个 CompletableFuture 的回调函数。由此来实现延迟
实现代码测试
private static void hasTimeOutAndError(List<CompletableFuture<String>> list) throws InterruptedException, ExecutionException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
sleep(2000);
System.out.println("future");
} catch (InterruptedException e) {
e.printStackTrace();
}
int a = 10 / 0;
return "ok";
});
CompletableFuture<String> completableFutureOne = CompletableFutureTimeoutUtil.completeOnTimeout("error", future, 1, TimeUnit.SECONDS, () -> System.out.println("log error 保存入库"));
list.add(completableFutureOne);
System.out.println("Test");
CompletableFuture<String> futureStr = CompletableFuture.supplyAsync(() -> {
try {
sleep(2000);
System.out.println("futureStr");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ok";
});
CompletableFuture<String> completableFutureTwo = CompletableFutureTimeoutUtil.completeOnTimeout("time out", futureStr, 1, TimeUnit.SECONDS, null);
list.add(completableFutureTwo);
CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()])).join();
System.out.println(completableFutureOne.get());
System.out.println(completableFutureTwo.get());
}
private static void allSuccess(List<CompletableFuture<String>> list) throws InterruptedException, ExecutionException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
sleep(2000);
System.out.println("future");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ok";
});
CompletableFuture<String> completableFutureOne = CompletableFutureTimeoutUtil.completeOnTimeout("error", future, 10, TimeUnit.SECONDS, () -> System.out.println("log error 保存入库"));
list.add(completableFutureOne);
System.out.println("Test");
CompletableFuture<String> futureStr = CompletableFuture.supplyAsync(() -> {
try {
sleep(2000);
System.out.println("futureStr");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ok";
});
CompletableFuture<String> completableFutureTwo = CompletableFutureTimeoutUtil.completeOnTimeout("time out", futureStr, 10, TimeUnit.SECONDS, null);
list.add(completableFutureTwo);
CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()])).join();
System.out.println(completableFutureOne.get());
System.out.println(completableFutureTwo.get());
}
Java9 内置实现方案
Java9 内置底层也是利用ScheduledThreadPoolExecutor 实现。具体可以参考Java9以及以上版本CompletableFuture 代码
实现代码测试
private static void java9PlusTimeOutAndError(List<CompletableFuture<String>> list) throws InterruptedException, ExecutionException {
CompletableFuture<String> completableFutureOne = CompletableFuture.supplyAsync(() -> {
try {
sleep(1);
System.out.println("completableFutureOne");
} catch (InterruptedException e) {
e.printStackTrace();
}
int a = 9 / 0;
return "completableFutureOne-ok";
}).completeOnTimeout("error", 3, TimeUnit.SECONDS).exceptionally(ex -> {
if (ex instanceof TimeoutException) {
System.out.println("Timeout occurred!");
} else if (ex instanceof CompletionException) {
System.out.println("Task exception: " + ex.getCause().getMessage());
} else if (ex instanceof RejectedExecutionException) {
System.out.println("Task rejected: " + ex.getMessage());
} else {
System.out.println("Unknown exception: " + ex.getMessage());
}
return "fallback";
});
;
list.add(completableFutureOne);
System.out.println("Test");
CompletableFuture<String> completableFutureTwo = CompletableFuture.supplyAsync(() -> {
try {
sleep(2000);
System.out.println("completableFutureTwo");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "completableFutureTwo-ok";
}).completeOnTimeout("error", 1, TimeUnit.SECONDS);
list.add(completableFutureTwo);
CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()])).join();
System.out.println(completableFutureOne.get());
System.out.println(completableFutureTwo.get());
}
测试
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
Stopwatch stopwatch = Stopwatch.createStarted();
List<CompletableFuture<String>> list = Lists.newArrayList();
java9PlusTimeOutAndError(list);
stopwatch.stop();
long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
System.out.println("耗时:" + elapsed);
}