如何在 Java 中实现同步方法超时?
Posted
技术标签:
【中文标题】如何在 Java 中实现同步方法超时?【英文标题】:How to implement synchronous method timeouts in Java? 【发布时间】:2013-06-18 10:55:02 【问题描述】:我有一个同步执行路径,它需要在给定的时间范围内完成或超时。假设我有一个带有 main() 方法的类,在该类中我调用方法 A(),该方法又调用 B(),而该方法又调用相同或不同类的 C() .....所有同步而不使用外部资源,如数据库、Web 服务或文件系统(其中每个都可以使用 TxManager 或相应的超时 API 独立超时)。所以它更像是 CPU 或内存密集型计算。如何在 Java 中为其超时编写代码?
我查看了 TimerTask,但更多的是使流异步和调度任务。还有其他建议吗?
【问题讨论】:
ExecutorService
和 Callable
s 可能是!!!
这是一个迭代任务吗?如果您的超时已到,您能否检查每次迭代?
我更多的是寻找一个通用的解决方案,而不是让它具体实现......因为可能有一个计算可能需要足够长的时间才能完成。
【参考方案1】:
您可以运行一个并行线程,该线程将等待指定的超时并中断当前线程,然后运行A()
。但是a、b、c必须是可中断的,即定期检查当前线程的中断标志并抛出InterruptedException,否则将不起作用
final Thread current = Thread.currentThread();
Thread timer = new Thread()
public void run()
try
Thread.sleep(5000);
current.interrupt();
catch (InterruptedException e)
// timer stopped
;
;
try
A(); // this throws InterruptedException if interrupted by timer
timer.interrupt(); // no timeout lets stop the timer
catch (InterruptedException e)
// timeout
【讨论】:
【参考方案2】:您应该使用ExecutorService 来做到这一点
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new Callable()
public String call() throws Exception
//do operations you want
return "OK";
);
try
System.out.println(future.get(2, TimeUnit.SECONDS)); //timeout is in 2 seconds
catch (TimeoutException e)
System.err.println("Timeout");
executor.shutdownNow();
【讨论】:
这实际上会停止 Callable 还是只是让 get() 超时? 只是get的“超时”,你需要通过“cancel(true)”方法或者调用executor.shutdownNow()来中断future【参考方案3】:另见post 该方法是让您的应用程序在其逻辑中处理超时。为此,您可以定义一些计时器类和特殊检查方法,例如:
public class TimeoutApp
MyTimer timer;
Thread timerThread;
public static void main(String... args)
new TimeoutApp().execute();
private void execute()
try
startTimer(1000);
action1();
checkTimeout();
action2();
checkTimeout();
action3();
stopTimer();
catch (MyTimeoutException e)
System.out.println("Interrupted on timeout!");
// ...clearing code if needed
System.exit(1);
catch (InterruptedException e)
System.out.println("Interrupted by exception!");
// ...clearing code if needed
e.printStackTrace();
System.exit(1);
private void action1() throws InterruptedException
Thread.sleep(600);
System.out.println("action 1");
private void action2() throws InterruptedException
Thread.sleep(500);
System.out.println("action 2");
private void action3()
System.out.println("action 3");
private void checkTimeout() throws MyTimeoutException
if (timer.isTimeoutReached())
throw new MyTimeoutException();
private void startTimer(long timeout)
timer = new MyTimer(timeout);
timerThread = new Thread(timer);
timerThread.start();
private void stopTimer()
timerThread.interrupt();
private class MyTimer implements Runnable
private long timeout;
private boolean timeoutReached = false;
public MyTimer(long timeout)
this.timeout = timeout;
public void run()
long time = System.currentTimeMillis();
while (!timeoutReached && !Thread.interrupted())
if ((System.currentTimeMillis() - time) > timeout)
timeoutReached = true;
public boolean isTimeoutReached()
return timeoutReached;
private class MyTimeoutException extends Exception
【讨论】:
非常低效。如果您的 action2() 是一个长时间运行的操作并且在其执行之间发生超时怎么办?【参考方案4】:您无法使用超时进行同步调用,但您可以使用第二个线程来模拟它。这是一个例子:
package com.ardevco.example;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
class ExceptionThrower
public static <R> R throwUnchecked(Throwable t)
return ExceptionThrower.<RuntimeException, R> trhow0(t);
@SuppressWarnings("unchecked")
private static <E extends Throwable, R> R trhow0(Throwable t) throws E
throw (E) t;
class TestApplicationException1 extends Exception
private static final long serialVersionUID = 1L;
public TestApplicationException1(String string)
super(string);
;
class TestApplicationException2 extends Exception
private static final long serialVersionUID = 1L;
public TestApplicationException2(String string)
super(string);
;
class TestApplicationTimeoutException extends Exception
private static final long serialVersionUID = 1L;
public TestApplicationTimeoutException(String string)
super(string);
;
public class SynchronousTimeoutTester
public static final long SYNC_METHOD_TIMEOUT_IN_MILLISECONDS = 2000L;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void main(String[] args)
SynchronousTimeoutTester tester = new SynchronousTimeoutTester();
/* call the method asynchronously 10 times */
for (int i = 0; i < 10; i++)
try
System.out.println("Result sync call: " + tester.getAsynchTest());
catch (TestApplicationException1 e)
System.out.println("catched as TestApplicationException1: " + e);
catch (TestApplicationException2 e)
System.out.println("catched as TestApplicationException2: " + e);
catch (TestApplicationTimeoutException e)
System.out.println("catched as TestApplicationTimeoutException: " + e);
catch (InterruptedException e)
System.out.println("catched as InterruptedException: " + e);
catch (Exception e)
System.out.println("catched as Exception: " + e);
tester.shutdown();
private void shutdown()
executorService.shutdown();
try
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
catch (InterruptedException e)
System.out.println("Error stopping threadpool:" + e);
private Integer testAsynch() throws TestApplicationException1, TestApplicationException2, InterruptedException
Random random = new Random();
switch (random.nextInt(10))
case 0:
return 0;
case 1:
throw new TestApplicationException1("thrown TestApplicationException1");
case 2:
throw new TestApplicationException2("thrown TestApplicationException2");
case 3:
Thread.sleep(10000L);
return -1;
case 4:
throw new RuntimeException("thrown Exception");
default:
return random.nextInt(10);
private Integer getAsynchTest() throws TestApplicationException1, TestApplicationException2, Exception
Integer dummy = null;
Future<Integer> testAsynchF = executorService.submit(
new Callable<Integer>()
public Integer call() throws Exception
return testAsynch();
);
try
dummy = testAsynchF.get(SynchronousTimeoutTester.SYNC_METHOD_TIMEOUT_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
catch (ExecutionException e1)
System.out.println("in getAsynchTest: ExecutionException: " + e1);
ExceptionThrower.throwUnchecked(e1.getCause());
catch (TimeoutException e1)
System.out.println("in getAsynchTest: TimeoutException: " + e1);
throw new TestApplicationTimeoutException("TimeoutException" + e1);
catch (InterruptedException e1)
System.out.println("in getAsynchTest: InterruptedException: " + e1);
throw new Exception(e1);
return dummy;
【讨论】:
以上是关于如何在 Java 中实现同步方法超时?的主要内容,如果未能解决你的问题,请参考以下文章