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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java:在某段代码上设置超时?相关的知识,希望对你有一定的参考价值。

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

答案

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

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

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

另一答案

而不是在主线程中的新线程和计时器中具有任务,而是在新线程中具有计时器并且在主线程中具有任务:

public static class TimeOut implements Runnable{
    public void run() {
        Thread.sleep(10000);
        if(taskComplete ==false) {
            System.out.println("Timed Out");
            return;
        }
        else {
            return;
        }
    }
}
public static boolean taskComplete = false;
public static void main(String[] args) {
    TimeOut timeOut = new TimeOut();
    Thread timeOutThread = new Thread(timeOut);
    timeOutThread.start();
    //task starts here
    //task completed
    taskComplete =true;
    while(true) {//do all other stuff }
}
另一答案

我遇到了类似的问题,我的任务是在特定超时内将消息推送到SQS。我使用了通过另一个线程执行它并通过指定超时等待其未来对象的简单逻辑。如果超时,这会给我一个TIMEOUT例外。

final Future<ISendMessageResult> future = 
timeoutHelperThreadPool.getExecutor().submit(() -> {
  return getQueueStore().sendMessage(request).get();
});
try {
  sendMessageResult = future.get(200, TimeUnit.MILLISECONDS);
  logger.info("SQS_PUSH_SUCCESSFUL");
  return true;

} catch (final TimeoutException e) {
  logger.error("SQS_PUSH_TIMEOUT_EXCEPTION");
}

但是有些情况下你无法阻止另一个线程执行的代码,在这种情况下你会得到真正的否定。

例如 - 在我的情况下,我的请求到达了SQS,并且在推送消息时,我的代码逻辑遇到了指定的超时。现在实际上我的消息被推入了队列,但由于TIMEOUT异常,我的主线程认为它失败了。这是一种可以避免而不是解决的问题。就像我的情况一样,我通过提供超时来避免它,这几乎在所有情况下都足够了。

如果您想要中断的代码在您的应用程序中并且不是API调用,那么您可以简单地使用

future.cancel(true)

但请记住,java文档说它确实可以保证执行被阻止。

“尝试取消执行此任务。如果任务已经完成,已被取消或由于某些其他原因无法取消,则此尝试将失败。如果成功,并且在调用取消时此任务尚未启动,则任务永远不会运行。如果任务已经启动,那么mayInterruptIfRunning参数确定执行此任务的线程是否应该被中断以试图停止任务。“

另一答案

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

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.

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

new TimeLimitedCodeBlock(5, TimeUnit.MINUTES) { @Override public void codeBlock() {
    // Do stuff here.
}}.run();
另一答案

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

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);
      }
    }
  }

}

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

  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
", elapsedSeconds, Thread.currentThread().getName(), msg);
  }

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

    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!
另一答案

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

@Test(timeout = 1000)  
public void shouldTakeASecondOrLess()
{
}

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

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

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

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

    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();
}
另一答案

我可以建议两种选择。

  1. 在该方法中,假设它是循环而不是等待外部事件,添加本地字段并在每次循环周围测试时间。 void method() { long endTimeMillis = System.currentTimeMillis() + 10000; while (true) { // method logic if (System.currentTimeMillis() > endTimeMillis) { // do some clean-up return; } } }
  2. 在线程中运行该方法,并将调用者计数为10秒。 Thread thread = new Thread(new Runnable() { @Override public void run() { method(); } }); thread.start(); long endTimeMillis = System.currentTimeMillis() + 10000; while (thread.isAlive()) { if (System.currentTimeMillis() > endTimeMillis) { // set an error flag break; } try { Thread.sleep(500); } catch (InterruptedException t) {} }

这种方法的缺点是method()不能直接返回值,它必须更新实例字段以返回其值。

另一答案

编辑:Peter Lawrey是完全正确的:它不像打断一个线程那么简单(我的原始建议),Executors&Callables非常有用......

一旦达到超时,您可以在Callable上设置变量,而不是中断线程。 callable应在任务执行的适当位置检查此变量,以了解何时停止。

Callables返回Futures,当你试图“获得”未来的结果时,你可以用它来指定超时。像这样的东西:

try {
  

以上是关于Java:在某段代码上设置超时?的主要内容,如果未能解决你的问题,请参考以下文章

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

如何延迟android的某段代码执行时间

nzSQLException 读取超时错误

根据图片的url地址下载图片到本地保存代码片段

transition啥意思

如何在java中的单行代码上设置计时器