我有一个同步执行路径,需要在给定的时间范围内完成或超时。假设我有一个带有 main() 方法的类,在该类中我调用方法 A(),该方法又调用 B(),而该方法又调用相同或不同类的 C() .....所有同步而不使用外部资源,如数据库、Web 服务或文件系统(其中每个都可以使用 TxManager 或相应的超时 API 独立超时)。所以它更像是 CPU 或内存密集型计算。如何在 Java 中为它的超时编写代码?
我看过 TimerTask,但更多的是使流程异步和调度任务。还有其他建议吗?
我有一个同步执行路径,需要在给定的时间范围内完成或超时。假设我有一个带有 main() 方法的类,在该类中我调用方法 A(),该方法又调用 B(),而该方法又调用相同或不同类的 C() .....所有同步而不使用外部资源,如数据库、Web 服务或文件系统(其中每个都可以使用 TxManager 或相应的超时 API 独立超时)。所以它更像是 CPU 或内存密集型计算。如何在 Java 中为它的超时编写代码?
我看过 TimerTask,但更多的是使流程异步和调度任务。还有其他建议吗?
你应该使用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();
您可以运行一个并行线程,该线程将等待指定的超时并中断当前线程,然后运行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
}
您不能使用超时进行同步调用,但您可以使用第二个线程来模拟它。这是一个例子:
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;
}
}
See also this post The approach is let your application care of timeout inside its logic. For that you can define some timer class and special checking method, e.g.:
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 {
}
}