Java:在某段代码上设置超时?

htf*_*htf 62 java timeout

在某些代码块运行时间超过可接受范围后,是否可以强制Java抛出异常?

小智 46

这是我所知道的最简单的方法:

final Runnable stuffToDo = new Thread() {
  @Override 
  public void run() { 
    /* Do stuff here. */ 
  }
};

final ExecutorService executor = Executors.newSingleThreadExecutor();
final Future future = executor.submit(stuffToDo);
executor.shutdown(); // This does not cancel the already-scheduled task.

try { 
  future.get(5, TimeUnit.MINUTES); 
}
catch (InterruptedException ie) { 
  /* Handle the interruption. Or ignore it. */ 
}
catch (ExecutionException ee) { 
  /* Handle the error. Or ignore it. */ 
}
catch (TimeoutException te) { 
  /* Handle the timeout. Or ignore it. */ 
}
if (!executor.isTerminated())
    executor.shutdownNow(); // If you want to stop the code that hasn't finished.
Run Code Online (Sandbox Code Playgroud)

或者,您可以创建一个TimeLimitedCodeBlock类来包装此功能,然后您可以在任何需要的地方使用它,如下所示:

new TimeLimitedCodeBlock(5, TimeUnit.MINUTES) { @Override public void codeBlock() {
    // Do stuff here.
}}.run();
Run Code Online (Sandbox Code Playgroud)


Nee*_*aks 35

我将一些其他答案编译成单个实用程序方法:

public class TimeLimitedCodeBlock {

  public static void runWithTimeout(final Runnable runnable, long timeout, TimeUnit timeUnit) throws Exception {
    runWithTimeout(new Callable<Object>() {
      @Override
      public Object call() throws Exception {
        runnable.run();
        return null;
      }
    }, timeout, timeUnit);
  }

  public static <T> T runWithTimeout(Callable<T> callable, long timeout, TimeUnit timeUnit) throws Exception {
    final ExecutorService executor = Executors.newSingleThreadExecutor();
    final Future<T> future = executor.submit(callable);
    executor.shutdown(); // This does not cancel the already-scheduled task.
    try {
      return future.get(timeout, timeUnit);
    }
    catch (TimeoutException e) {
      //remove this if you do not want to cancel the job in progress
      //or set the argument to 'false' if you do not want to interrupt the thread
      future.cancel(true);
      throw e;
    }
    catch (ExecutionException e) {
      //unwrap the root cause
      Throwable t = e.getCause();
      if (t instanceof Error) {
        throw (Error) t;
      } else if (t instanceof Exception) {
        throw (Exception) t;
      } else {
        throw new IllegalStateException(t);
      }
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

使用此实用程序方法的示例代码:

  public static void main(String[] args) throws Exception {
    final long startTime = System.currentTimeMillis();
    log(startTime, "calling runWithTimeout!");
    try {
      TimeLimitedCodeBlock.runWithTimeout(new Runnable() {
        @Override
        public void run() {
          try {
            log(startTime, "starting sleep!");
            Thread.sleep(10000);
            log(startTime, "woke up!");
          }
          catch (InterruptedException e) {
            log(startTime, "was interrupted!");
          }
        }
      }, 5, TimeUnit.SECONDS);
    }
    catch (TimeoutException e) {
      log(startTime, "got timeout!");
    }
    log(startTime, "end of main method!");
  }

  private static void log(long startTime, String msg) {
    long elapsedSeconds = (System.currentTimeMillis() - startTime);
    System.out.format("%1$5sms [%2$16s] %3$s\n", elapsedSeconds, Thread.currentThread().getName(), msg);
  }
Run Code Online (Sandbox Code Playgroud)

在我的机器上运行示例代码的输出:

    0ms [            main] calling runWithTimeout!
   13ms [ pool-1-thread-1] starting sleep!
 5015ms [            main] got timeout!
 5016ms [            main] end of main method!
 5015ms [ pool-1-thread-1] was interrupted!
Run Code Online (Sandbox Code Playgroud)

  • 现在,我添加了示例代码并改进了原始代码,以在超时的情况下取消已提交的任务。我会很感激;-) (2认同)

Pet*_*rey 21

是的,但强制另一个线程在随机代码行中断通常是一个非常糟糕的主意.如果您打算关闭该过程,则只会执行此操作.

你可以做的是Thread.interrupt()在一定时间后用于任务.但是,除非代码检查它,否则它将无法工作.ExecutorService可以使这更容易Future.cancel(true)

它更好地使代码自己计时并在需要时停止.

  • 遗憾的是,这是一个常见的问题,唯一可行的方法就是拥有一个可以杀死的独立过程.另一种方法是在其上使用Thread.stop().在使用之前,请阅读此方法的警告! (2认同)

mdm*_*dma 6

如果是您想要时间的测试代码,那么您可以使用以下time属性:

@Test(timeout = 1000)  
public void shouldTakeASecondOrLess()
{
}
Run Code Online (Sandbox Code Playgroud)

如果它是生产代码,则没有简单的机制,您使用哪种解决方案取决于您是否可以更改要定时的代码.

如果您可以更改定时代码,那么一个简单的方法是让您的定时代码记住它的开始时间,并定期记录当前时间.例如

long startTime = System.currentTimeMillis();
// .. do stuff ..
long elapsed = System.currentTimeMillis()-startTime;
if (elapsed>timeout)
   throw new RuntimeException("tiomeout");
Run Code Online (Sandbox Code Playgroud)

如果代码本身无法检查超时,则可以在另一个线程上执行代码,并等待完成或超时.

    Callable<ResultType> run = new Callable<ResultType>()
    {
        @Override
        public ResultType call() throws Exception
        {
            // your code to be timed
        }
    };

    RunnableFuture future = new FutureTask(run);
    ExecutorService service = Executors.newSingleThreadExecutor();
    service.execute(future);
    ResultType result = null;
    try
    {
        result = future.get(1, TimeUnit.SECONDS);    // wait 1 second
    }
    catch (TimeoutException ex)
    {
        // timed out. Try to stop the code if possible.
        future.cancel(true);
    }
    service.shutdown();
}
Run Code Online (Sandbox Code Playgroud)