【问题标题】:How to implement synchronous method timeouts in Java?如何在 Java 中实现同步方法超时?
【发布时间】:2013-06-18 10:55:02
【问题描述】:

我有一个同步执行路径,它需要在给定的时间范围内完成或超时。假设我有一个带有 main() 方法的类,在该类中我调用方法 A(),该方法又调用 B(),而该方法又调用相同或不同类的 C() .....所有同步而不使用外部资源,如数据库、Web 服务或文件系统(其中每个都可以使用 TxManager 或相应的超时 API 独立超时)。所以它更像是 CPU 或内存密集型计算。如何在 Java 中为其超时编写代码?

我查看了 TimerTask,但更多的是使流异步和调度任务。还有其他建议吗?

【问题讨论】:

  • ExecutorServiceCallables 可能是!!!
  • 这是一个迭代任务吗?如果您的超时已到,您能否检查每次迭代?
  • 我更多的是寻找一个通用的解决方案,而不是让它具体实现......因为可能有一个计算可能需要足够长的时间才能完成。

标签: java timeout synchronous


【解决方案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;
       }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2017-12-08
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多