Java:如何实现代码超时功能?

 玉米猴子_794 发布于 2022-10-29 00:12
public static void function() {
    // 代码1
    // 代码2
    // 代码3
}

如果代码2执行时间过长则不再执行(代码2没有抛出TimeoutException,只是没按照规定时间执行完),继续执行后面的代码3该如何实现呢?

下面是代码超时功能的一种实现

   public class Timeout {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService exec = Executors.newFixedThreadPool(1);
        Callable call = new Callable() {
            public Integer call() throws Exception {
                Thread.sleep(1000 * 5);// 耗时操作
                return 1;
            }
        };
        try {
            Future future = exec.submit(call);
            int ret = future.get(1000 * 1, TimeUnit.MILLISECONDS); // 任务处理超时时间设为 1 秒
            System.out.println("任务执行结果:" + ret);
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        exec.shutdown();
    }
}

但这种方法的问题是新启动了一个线程,并没有阻塞,也就是我的代码3可能先于Timeout执行完,顺序不满足预期,前辈有什么好办法呢?

2 个回答
  • 我们写一个有超时功能的 Callable:

    import java.util.concurrent.*;
    
    public class TimeoutCallable<V> implements Callable<V> {
    
        private final Callable<V> callable;
        private final V timeoutV;
        private final long timeout;
    
        /**
         * 构造一个 TimeoutCallable
         *
         * @param callable 要运行的 Callable
         * @param timeout Callable 的最大运行时间
         * @param timeoutV Callable 超时的返回结果
         */
        public TimeoutCallable(Callable<V> callable, long timeout, V timeoutV) {
            this.timeout = timeout;
            this.callable = callable;
            this.timeoutV = timeoutV;
        }
    
        @Override
        public V call() throws Exception {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<V> future = executor.submit(callable);
    
            V v = null;
            try {
                v = future.get(timeout, TimeUnit.MILLISECONDS);
            } catch (TimeoutException ex) {
                System.out.println("Callble 超时");
            }
    
            executor.shutdownNow(); // 给线程池中所有正在运行的线程发送 中断 信号
    
            return v != null ? v : timeoutV;
        }
    
    }
    

    然后试验:

    import java.util.concurrent.*;
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
    
            Callable<Integer> callable = () -> {
                int N = 4;
                int sum = 0;
                for (int i = 0; i < N; i++) {
                    // Thread.sleep 方法是可以响应中断的,
                    // 如果你的代码需要“超时则线程结束”的效果,那么你的代码也应该要能够响应中断
                    Thread.sleep(1000);
                    sum += i;
                }
    
                return sum;
            };
    
            // 代码2, 代码2 运行的最长时间为 timeout
            int timeout = 3000;
            Integer timeoutValue = -1;
            TimeoutCallable<Integer> timeoutCallable = new TimeoutCallable<>(callable, timeout, timeoutValue);
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<Integer> future = executor.submit(timeoutCallable);
    
            Integer result = future.get();
            executor.shutdown();
            // end 代码2
    
            // 代码3
            if (timeoutValue.equals(result)) {
                System.out.println("--任务超时--");
            } else {
                System.out.println("任务结果:" + result);
            }
            // end 代码3
        }
    }
    

    callable 的运行时间为 4 s,但我们设置的超时时间为 3 s,所以代码运行结果就是:
    (可以看到 NetBeans 给出的运行时间是 3 s)

    如果我们将 timeout 改成 5000(5 s),则结果是:
    (可以看到 NetBeans 给出的运行时间是 4 s)

    2022-11-12 01:45 回答
  • 写了个小例子,可以简单实现你的要求

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    
    public class Main {
    
        public static void main(String[] args) throws InterruptedException {
            ExecutorService es = Executors.newFixedThreadPool(1);
            es.execute(new Runnable() {
                @Override
                public void run() {
                    int count = 7;
                    while (count > 0) {
                        System.out.println(1);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // 退出执行
                            System.out.println("interrupt, then quit");
                            return;
                        }
                        count--;
                    }
                }
            });
            // 关闭线程池
            es.shutdown();
            // 阻塞操作,等待5s
            boolean finished = es.awaitTermination(5, TimeUnit.SECONDS);
            // 如果过了5s线程还没有完成, 强制关闭, interrupt Runnable 线程,  进入 InterruptedException 处理流程
            if (!finished) {
                es.shutdownNow();
            }
            System.out.println("task 3");
        }
    }
    
    2022-11-12 01:45 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有