Подтвердить что ты не робот

Рекомендации Java Executor для задач, которые должны выполняться навсегда

Я работаю над проектом Java, где мне нужно, чтобы несколько задач выполнялись асинхронно. Я убежден, что Исполнитель - лучший способ для меня сделать это, поэтому я знакомлюсь с ним. (Я получаю деньги, чтобы учиться!) Однако мне непонятно, какой лучший способ - выполнить то, что я пытаюсь сделать.

Для аргумента предположим, что у меня две задачи. Ожидается, что ни один из них не завершится, и оба должны работать в течение срока действия приложения. Я пытаюсь написать основной класс-оболочку, чтобы:

  • Если какая-либо из задач выдает исключение, оболочка поймает ее и перезапустит задачу.
  • Если любая задача выполняется до завершения, оболочка заметит и перезапустит задачу.

Теперь следует отметить, что реализация для обеих задач будет завершать код в run() в бесконечном цикле, который никогда не будет выполняться до завершения, с блоком try/catch, который должен обрабатывать все исключения во время выполнения без нарушения цикла, Я пытаюсь добавить еще один уровень уверенности; если я или кто-то, кто следует за мной, делает что-то глупое, которое побеждает эти гарантии и останавливает задачу, приложение должно реагировать соответствующим образом.

Есть ли наилучшая практика для решения этой проблемы, которую люди, более опытные, чем я, рекомендуют?

FWIW, я взломал этот тестовый класс:


public class ExecTest {

   private static ExecutorService executor = null;
   private static Future results1 = null;
   private static Future results2 = null;

   public static void main(String[] args) {
      executor = Executors.newFixedThreadPool(2);
      while(true) {
         try {
            checkTasks();
            Thread.sleep(1000);
         }
         catch (Exception e) {
            System.err.println("Caught exception: " + e.getMessage());
         }
      }
   }

   private static void checkTasks() throws Exception{
      if (results1 == null || results1.isDone() || results1.isCancelled()) {
         results1 = executor.submit(new Test1());
      }

      if (results2 == null || results2.isDone() || results2.isCancelled()) {
         results2 = executor.submit(new Test2());
      }
   }
}

class Test1 implements Runnable {
   public void run() {
      while(true) {
         System.out.println("I'm test class 1");
         try {Thread.sleep(1000);} catch (Exception e) {}
      }

   }
}

class Test2 implements Runnable {
   public void run() {
      while(true) {
         System.out.println("I'm test class 2");
         try {Thread.sleep(1000);} catch (Exception e) {}
      }
   }
}

Он ведет себя так, как я хочу, но я не знаю, есть ли какие-либо ошибки, неэффективность или просто неправильная голова, ожидающая удивить меня. (На самом деле, учитывая, что я новичок в этом, я был бы потрясен, если бы не было что-то не так/нецелесообразно.)

Любое понимание приветствуется.

4b9b3361

Ответ 1

Я столкнулся с аналогичной ситуацией в своем предыдущем проекте, и после того, как мой код взорвался перед лицом сердитого клиента, мои приятели и я добавили двух больших охранников:

  • В бесконечном цикле поймать Ошибки тоже, а не только исключения. Иногда происходят непредвиденные события, и Java выдает вам ошибку, а не исключение.
  • Используйте резервный переключатель, поэтому, если что-то пойдет не так и не восстанавливается, вы не эскалируете ситуацию, нетерпеливо запуская другой цикл. Вместо этого вам нужно подождать, пока ситуация вернется к нормальному состоянию, а затем снова начнется.

Например, у нас была ситуация, когда база данных опустилась, и во время цикла было выбрано SQLException. Несчастливый результат заключался в том, что код снова прошел цикл, только чтобы снова повторить одно и то же исключение и так далее. Журналы показали, что мы попали в одно и то же SQLException примерно 300 раз в секунду!!... это случалось с перерывами несколько раз с временными паузами JVM продолжительностью 5 секунд или около того, во время которых приложение не реагировало, пока, в конце концов, не была выброшена ошибка и нить умерла!

Таким образом, мы реализовали стратегию отступления, примерно показанную в приведенном ниже коде, что если исключение не восстанавливается (или оно восстанавливается в течение нескольких минут), то мы ожидаем более длительного времени до возобновления операций.

class Test1 implements Runnable {
  public void run() {
    boolean backoff = false;
    while(true) {
      if (backoff) {
        Thread.sleep (TIME_FOR_LONGER_BREAK);
        backoff = false;
      }
      System.out.println("I'm test class 1");
      try {
        // do important stuff here, use database and other critical resources
      }
      catch (SqlException se) {
       // code to delay the next loop
       backoff = true;
      }
      catch (Exception e) {
      }
      catch (Throwable t) {
      }
    }
  }
}

Если вы выполняете свои задачи таким образом, я не вижу смысла иметь третий поток "сторожевая собака" с методом checkTasks(). Кроме того, по тем же причинам, о которых я говорил выше, я был бы осторожен, чтобы снова начать задание с исполнителем. Сначала вам нужно понять, почему задача не удалась, и будет ли среда в стабильном состоянии снова запускать задачу.

Ответ 2

Кроме того, я обычно запускаю Java-код против инструментов статического анализа, таких как PMD и FindBugs, чтобы искать более глубокие проблемы.

В частности, для этого кода FindBugs не понравилось, что результаты1 и результаты2 не являются изменчивыми в ленивом init, и что методы run() могут игнорировать исключение, потому что они явно не обрабатываются.

В общем, я немного склонен к использованию Thread.sleep для тестирования concurrency, предпочитая таймеры или прекращающие состояния/условия, Вызываемый может быть полезен при возвращении чего-либо в случае сбоя, который генерирует исключение, если не удается вычислить результат.

Для некоторых лучших практик и большего количества пищи для размышлений проверьте Concurrency на практике.

Ответ 4

как насчет этого

Runnable task = () -> {
  try{
    // do the task steps here
  } catch (Exception e){
    Thread.sleep (TIME_FOR_LONGER_BREAK);
  }    
};
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(task,0, 0,TimeUnit.SECONDS);