【问题标题】:Time out method in javajava中的超时方法
【发布时间】:2016-12-14 01:47:29
【问题描述】:

在 java 类中,我有一个有时需要很长时间才能执行的方法。也许它挂在那个方法流中。我想要的是,如果该方法没有在特定时间完成,程序应该退出该方法并继续其余流程。

请告诉我有什么办法可以处理这种情况。

【问题讨论】:

    标签: java


    【解决方案1】:

    您必须使用线程来实现这一点。线程是无害的 :) 下面的示例运行一段代码 10 秒然后结束它。

    public class Test {
        public static void main(String args[])
            throws InterruptedException {
    
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("0");
                    method();
                }
            });
            thread.start();
            long endTimeMillis = System.currentTimeMillis() + 10000;
            while (thread.isAlive()) {
                if (System.currentTimeMillis() > endTimeMillis) {
                    System.out.println("1");
                    break;
                }
                try {
                    System.out.println("2");
                    Thread.sleep(500);
                }
                catch (InterruptedException t) {}
            }
    
    
        }
    
        static void method() {
            long endTimeMillis = System.currentTimeMillis() + 10000;
            while (true) {
                // method logic
                System.out.println("3");
                if (System.currentTimeMillis() > endTimeMillis) {
                    // do some clean-up
                    System.out.println("4");
                    return;
                }
            }
        }
    }
    

    【讨论】:

    • 太棒了!对我来说非常完美。
    【解决方案2】:

    在不同的线程中执行方法,你可以随时结束一个线程。

    【讨论】:

    • 有什么办法不使用线程。我不想使用线程。
    • @Rana 我不想使用线程 为什么?如果您不熟悉,请查看java tutorial
    • 嗨,Sanjay,线程的问题是,如果我们有多个线程为同一部分代码运行,我们不确定线程​​流的执行,这似乎有点难以管理或调试.
    • 看看java.util.concurrent.*,尤其是FutureTaskCallableExecutors。请参阅此线程答案以获取示例:StackOverflow Question 240320
    • @Rana 为同一部分代码运行多个线程 ??顺便说一句,看看我在上一条评论中链接的教程。
    【解决方案3】:

    基于上面的snipplet,我尝试创建一个美化的spring bean。

    这样的执行器在有限的runtimeInMs中运行传递的limitedRuntimeTask。 如果任务在其时间限制内完成,调用者将继续正常执行。

    如果 limitedRuntimeTask 未能在定义的 runtimeInMs 内完成, 调用者将收到返回的线程执行。如果定义了 timeBreachedTask, 它将在返回给调用者之前执行。

    public class LimitedRuntimeExecutorImpl {
    
    
    public void runTaskInLessThanGivenMs(int runtimeInMs, final Callable limitedRuntimeTask, final Callable timeBreachedTask) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    LOGGER.info("Started limitedRuntimeTask");
                    limitedRuntimeTask.call();
                    LOGGER.info("Finished limitedRuntimeTask in time");
                } catch (Exception e) {
                    LOGGER.error("LimitedRuntimeTask exception", e);
                }
            }
        });
        thread.start();
    
        long endTimeMillis = System.currentTimeMillis() + runtimeInMs;
    
        while (thread.isAlive()) {
            if (System.currentTimeMillis() > endTimeMillis) {
                LOGGER.warn("LmitedRuntimeTask did not finish in time (" + runtimeInMs + ")ms. It will run in vain.");
                if(timeBreachedTask != null ){
                    try {
                        LOGGER.info("Executing timeBreachedTask");
                        timeBreachedTask.call();
                        LOGGER.info("Finished timeBreachedTask");
                    } catch (Exception e) {
                        LOGGER.error("timeBreachedTask exception", e);
                    }
                }
                return;
            }
            try {
                Thread.sleep(10);
            }
            catch (InterruptedException t) {}
        }
    
    }
    

    }

    【讨论】:

      【解决方案4】:

      我觉得接受答案的方法有点过时了。使用Java8,它可以做得更简单。

      说,你有方法

      MyResult conjureResult(String param) throws MyException { ... }
      

      那么你可以这样做(继续阅读,这只是为了展示方法):

      private final ExecutorService timeoutExecutorService = Executors.newSingleThreadExecutor();
      
      MyResult conjureResultWithTimeout(String param, int timeoutMs) throws Exception {
          Future<MyResult> future = timeoutExecutorService.submit(() -> conjureResult(param));
          return future.get(timeoutMs, TimeUnit.MILLISECONDS);
      }    
      

      当然,抛出异常是不好的,这里是正确的扩展版本和正确的错误处理,但我建议你仔细检查它,你可能想做一些不同的事情(日志记录,在扩展结果中返回超时等):

      private final ExecutorService timeoutExecutorService = Executors.newSingleThreadExecutor();
      
      MyResult conjureResultWithTimeout(String param, int timeoutMs) throws MyException {
          Future<MyResult> future = timeoutExecutorService.submit(() -> conjureResult(param));
          try {
              return future.get(timeoutMs, TimeUnit.MILLISECONDS);
          } catch (InterruptedException e) {
              //something interrupted, probably your service is shutting down
              Thread.currentThread().interrupt();
              throw new RuntimeException(e);
          } catch (ExecutionException e) {
              //error happened while executing conjureResult() - handle it
              if (e.getCause() instanceof MyException) {
                  throw (MyException)e.getCause();
              } else {
                  throw new RuntimeException(e);
              }
          } catch (TimeoutException e) {
              //timeout expired, you may want to do something else here
              throw new RuntimeException(e);
          }
      }
      

      【讨论】:

      • 我知道这晚了 4 年,但是这种设置方式意味着来自 ExecutorService 的线程仍然存在。这可能会导致一些不良行为。
      • @SvenT23 是的,但它已准备好处理另一个 submit()。显然,它应该在某些应用程序关闭挂钩、关闭侦听器等上关闭。理想情况下,线程池应该与关闭一起使用,但是线程池生命周期管理有点超出了问题的范围。尽管如此,良好的评论和提醒人们不要忘记它。
      猜你喜欢
      • 2023-03-10
      • 2014-03-27
      • 1970-01-01
      • 1970-01-01
      • 2013-06-18
      • 2019-06-29
      • 1970-01-01
      • 1970-01-01
      • 2014-11-17
      相关资源
      最近更新 更多