Установка максимального времени выполнения для метода / потока

#java #spring #spring-boot

#java #многопоточность #параллелизм

Вопрос:

У меня есть метод, который записывает данные в базу данных. Требование состоит в том, чтобы убедиться, что этот метод не выполняется по истечении определенного времени. Если он вернется до этого, то ничего не следует предпринимать.

Единственный базовый подход, который я могу придумать, — это сделать что-то подобное.

 public class LimitedRuntime {

    public static void writeToDb(){
            // writes to the database
    }

    public static void main(String[] args) {
        long totalExecutionTime = 8000L;
        long startTime = System.currentTimeMillis();

        while(System.currentTimeMillis() - startTime < totalExecutionTime )
        {
            writeToDb();
        }   
    }
}
 

Одна из проблем этого подхода заключается в том, что даже если метод возвращается раньше максимального общего времени выполнения, даже тогда программа останавливается, чтобы дождаться истечения этого времени.

Как я могу сделать это лучше (или, может быть, более правильно)? И если мы используем Thread , как мы можем узнать , какой Thread из них выполняет этот метод?

Комментарии:

1. Как насчет самого незаметного решения, Δt = t2 - t2 о.О

Ответ №1:

Вы можете сделать это, отправив свою работу исполнителю:

  public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(4);

    Future<?> future = executor.submit(new Runnable() {
        @Override
        public void run() {
            writeToDb();            //        <-- your job
        }
    });

    executor.shutdown();            //        <-- reject all further submissions

    try {
        future.get(8, TimeUnit.SECONDS);  //     <-- wait 8 seconds to finish
    } catch (InterruptedException e) {    //     <-- possible error cases
        System.out.println("job was interrupted");
    } catch (ExecutionException e) {
        System.out.println("caught exception: "   e.getCause());
    } catch (TimeoutException e) {
        future.cancel(true);              //     <-- interrupt the job
        System.out.println("timeout");
    }

    // wait all unfinished tasks for 2 sec
    if(!executor.awaitTermination(2, TimeUnit.SECONDS)){
        // force them to quit by interrupting
        executor.shutdownNow();
    }
}
 

Комментарии:

1. Прерывая поток, вы должны помнить, что вы должны добавить условие like if (isInterrupted()) { return; } . Без этого Поток вообще не будет прерван! Не знаю, как это будет работать с запускаемым интерфейсом…

2. Да, я предположил, что драйвер БД должен поддерживать это.

3. Я с этим не знаком ExecutorService . Я буду читать об этом. Кстати, не могли бы вы просто сказать мне а) Что произойдет выше, если метод завершится и вернется до истечения максимального времени? б) я хочу завершить работу метода мгновенно, как только истечет установленный срок. Для этого я должен использовать executor.awaitTermination(0, TimeUnit.SECONDS) ?

4. a) Будущее останавливается в ожидании завершения задания, main() поток продолжает выполнение. В этом конкретном случае приложение просто завершит работу. б) future.cancel(true) следует завершить поток. Поток должен поддерживать завершение, как отметил @bobbel. Ваш драйвер БД должен поддерживать его, если поток выполняет несколько запросов, он должен проверять Thread.interrupted() флаг между ними.

Ответ №2:

Для этого также существует решение AspectJ с библиотекой jcabi-aspects:

 @Timeable(limit = 5, unit = TimeUnit.SECONDS)
public String writeToDb() {
  // writeToDb
}
 

Существует статья, объясняющая это далее: Ограничить время выполнения метода Java