Как реализовать синхронные таймауты метода в Java?
У меня есть синхронный путь выполнения, который должен быть завершен или тайм-аут в течение заданного временного интервала. Скажем, у меня есть класс с методом main(), в котором я вызываю методы A(), которые в свою очередь вызывают B(), и которые в свою очередь вызывают C() одинаковых или разных классов..... все синхронно, не используя внешний ресурс, такой как база данных, webservice или файловая система (где каждый из них может быть синхронизирован независимо с помощью TxManager или соответствующего тайм-аута api). Таким образом, это больше похоже на интенсивное вычисление процессора или памяти. Как мне закодировать для него тайм-аут в Java?
Я просмотрел TimerTask, но это больше для создания асинхронного потока и для планирования задач. Любые другие предложения?
Ответы
Ответ 1
Вы должны использовать 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();
Ответ 2
Вы можете запустить параллельный поток, который будет ожидать указанный тайм-аут и прервать текущий поток, а затем запустите 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
}
Ответ 3
Синхронный вызов с таймаутом невозможен, но вы можете эмулировать его с помощью второго потока. Это пример для этого:
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;
}
}
Ответ 4
См. также сообщение . Подход позволяет вашему приложению заботиться о тайм-ауте внутри своей логики. Для этого вы можете определить некоторый тип таймера и специальный метод проверки, например:
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 {
}
}