Как реализовать синхронные таймауты метода в 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 {
    }
}