以下参考JDK17中 orTimeout的实现
package com.example.demo.test.thread;
import java.util.concurrent.*;
import java.util.function.BiConsumer;
/**
* CompletableFuture 扩展工具
*/
public class CompletableFutureUtils {
/**
* 如果在给定超时之前未完成,则异常完成此 CompletableFuture 并抛出 {@link TimeoutException} 。
*
* @param timeout 在出现 TimeoutException 异常完成之前等待多长时间,以 {@code unit} 为单位
* @param unit 一个 {@link TimeUnit},结合 {@code timeout} 参数,表示给定粒度单位的持续时间
* @return 入参的 CompletableFuture
*/
public static <T> CompletableFuture<T> within(CompletableFuture<T> future, long timeout, TimeUnit unit) {
if (null == unit) {
throw new RuntimeException("给定的时间粒度不能为空");
}
if (null == future) {
throw new RuntimeException("异步任务不能为空");
}
if (future.isDone()) {
return future;
}
return future.whenComplete(new Canceller(Delayer.delay(new Timeout(future), timeout, unit)));
}
/**
* 超时时异常完成的操作
*/
static final class Timeout implements Runnable {
final CompletableFuture<?> future;
Timeout(CompletableFuture<?> future) {
this.future = future;
}
public void run() {
if (null != future && !future.isDone()) {
future.completeExceptionally(new TimeoutException());
}
}
}
/**
* 取消不需要的超时的操作
*/
static final class Canceller implements BiConsumer<Object, Throwable> {
final Future<?> future;
Canceller(Future<?> future) {
this.future = future;
}
public void accept(Object ignore, Throwable ex) {
if (null == ex && null != future && !future.isDone()) {
future.cancel(false);
}
}
}
/**
* 单例延迟调度器,仅用于启动和取消任务,一个线程就足够
*/
static final class Delayer {
static ScheduledFuture<?> delay(Runnable command, long delay, TimeUnit unit) {
return delayer.schedule(command, delay, unit);
}
static final class DaemonThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("DelayScheduler");
return t;
}
}
static final ScheduledThreadPoolExecutor delayer;
static {
delayer = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory());
delayer.setRemoveOnCancelPolicy(true);
}
}
}
后记
1. 单个线程时没有问题
@Test
void testCompeletableTimeout() throws InterruptedException {
CompletableFutureUtils.within(CompletableFuture.runAsync(() -> {
try {
System.out.println("----模拟事务处理耗时-----");
TimeUnit.MILLISECONDS.sleep(10000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}), 1, TimeUnit.SECONDS).join();
TimeUnit.SECONDS.sleep(2);
System.out.println("正确运行");
}
2. 多线程有问题,超时时间变成整体运行的超时时间
@Test
void testCompeletableTimeout() throws InterruptedException {
CompletableFuture.allOf(
IntStream.range(0, 100).mapToObj(num ->
CompletableFutureUtils.within(CompletableFuture.runAsync(() -> {
try {
System.out.println("----模拟远程调用耗时-----");
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}), 1, TimeUnit.SECONDS)).toArray(CompletableFuture[]::new)
).join();
TimeUnit.SECONDS.sleep(2);
System.out.println("正确运行");
}
按说每个远程调用都是100毫秒返回,超时时间1s,应该不会超时,实际执行结果
线程在生成是已经放入延迟队列,开始计时