JDK8中实现 CompletableFuture超时处理遇到的坑

1 篇文章 0 订阅
1 篇文章 0 订阅

以下参考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,应该不会超时,实际执行结果
在这里插入图片描述
线程在生成是已经放入延迟队列,开始计时

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值