ScheduledExecutorService Обработка исключений

Я использую ScheduledExecutorService для периодического выполнения метода.

p-code:

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> handle =
        scheduler.scheduleWithFixedDelay(new Runnable() {
             public void run() { 
                 //Do business logic, may Exception occurs
             }
        }, 1, 10, TimeUnit.SECONDS);

Мой вопрос:

Как продолжить работу планировщика, если run () вызывает исключение? Должен ли я попытаться перехватить все исключения в методе run () ? Или любой встроенный метод обратного вызова для обработки исключения? Спасибо!

52
задан 卢声远 Shengyuan Lu 31 July 2011 в 18:45
поделиться

1 ответ

Старый вопрос, но принятый ответ не дает объяснения и обеспечивает плохой пример, и большая часть ответа upvoted является правильной на некоторых точках, но наконец поощряет Вас добавлять catch исключения в каждом Runnable.run() метод.
я не соглашаюсь потому что:

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

я думаю, что распространение исключения должно быть выполнено ExecutorService платформа, и на самом деле это предлагает ту функцию.
Кроме того, попытка быть слишком умным путем попытки к замыканию накоротко ExecutorService способом работать не является хорошая идея также: платформа может развиться, и Вы хотите использовать ее стандартным способом.
Наконец, позволяя ExecutorService платформа для создания ее задания не означает обязательно останавливать последующую задачу вызовов.
, Если запланированная задача встречается с проблемой, которая является обязанностью вызывающей стороны перенести или не задача согласно причине проблемы.
Каждый слой имеет его свои обязанности. Хранение их делает код и четким и удобным в сопровождении.

<час>

ScheduledFuture.get (): правильный API для ловли исключений и ошибок произошел в состоянии задачи

ScheduledExecutorService.scheduleWithFixedDelay()/scheduleAtFixRate() в их спецификации:

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

Это означает, что ScheduledFuture.get() не возвращается при каждом запланированном вызове, но что это возвращается для последнего вызова задачи, которая является отменой задачи: вызванный [1 112] или исключение, добавленное задача.
Настолько обрабатывающий эти ScheduledFuture возврат для получения исключения с [1 114] выглядит правильным:

  try {
    future.get();

  } catch (InterruptedException e) {
    // ... to handle
  } catch (ExecutionException e) {
    // ... and unwrap the exception OR the error that caused the issue
    Throwable cause = e.getCause();       
  }
<час>

Пример с поведением по умолчанию: остановка планирования, если одно из выполнения задачи встречается с выпуском

, Это выполняет задачу, что для третьего выполнения, выданного, исключение и завершает планирование. В некоторых сценариях мы хотим это.

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ScheduledExecutorServiceWithException {

  public static void main(String[] args) {
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);

    // variable used to thrown an error at the 3rd task invocation
    AtomicInteger countBeforeError = new AtomicInteger(3);

    // boolean allowing to leave the client to halt the scheduling task or not after a failure
    Future<?> futureA = executor
        .scheduleWithFixedDelay(new MyRunnable(countBeforeError), 1, 2, TimeUnit.SECONDS);
    try {
      System.out.println("before get()");
      futureA.get(); // will return only if canceled
      System.out.println("after get()");
    } catch (InterruptedException e) {
      // handle that : halt or no
    } catch (ExecutionException e) {
      System.out.println("exception caught :" + e.getCause());
    }

    // shutdown the executorservice
    executor.shutdown();
  }

  private static class MyRunnable implements Runnable {

    private final AtomicInteger invocationDone;

    public MyRunnable(AtomicInteger invocationDone) {
      this.invocationDone = invocationDone;
    }

    @Override
    public void run() {
      System.out.println(Thread.currentThread().getName() + ", execution");
      if (invocationDone.decrementAndGet() == 0) {
        throw new IllegalArgumentException("ohhh an Exception in MyRunnable");
      }
    }
  }
}

Вывод:

before get()
pool-1-thread-1, execution
pool-1-thread-1, execution
pool-1-thread-1, execution
exception caught :java.lang.IllegalArgumentException: ohhh an Exception in MyRunnable

Пример с возможностью пойти на планирование, если одно из выполнения задачи встречается с выпуском

, Это выполняет задачу, которая выдает исключение при двух первом выполнении и бросает ошибку в третью. Мы видим, что клиент задач может принять решение остановиться или не планирование: здесь я продолжаю в случаях исключения, и я останавливаюсь в случае ошибки.

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ScheduledExecutorServiceWithException {

  public static void main(String[] args) {
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);

    // variable used to thrown an error at the 3rd task invocation
    AtomicInteger countBeforeError = new AtomicInteger(3);

    // boolean allowing to leave the client to halt the scheduling task or not after a failure
    boolean mustHalt = true;
    do {
      Future<?> futureA = executor
              .scheduleWithFixedDelay(new MyRunnable(countBeforeError), 1, 2, TimeUnit.SECONDS);
      try {
        futureA.get(); // will return only if canceled
      } catch (InterruptedException e) {
        // handle that : halt or not halt
      } catch (ExecutionException e) {
        if (e.getCause() instanceof Error) {
          System.out.println("I halt in case of Error");
          mustHalt = true;
        } else {
          System.out.println("I reschedule in case of Exception");
          mustHalt = false;
        }
      }
    }
    while (!mustHalt);
    // shutdown the executorservice
    executor.shutdown();
  }

  private static class MyRunnable implements Runnable {

    private final AtomicInteger invocationDone;

    public MyRunnable(AtomicInteger invocationDone) {
      this.invocationDone = invocationDone;
    }

    @Override
    public void run() {
      System.out.println(Thread.currentThread().getName() + ", execution");

      if (invocationDone.decrementAndGet() == 0) {
        throw new Error("ohhh an Error in MyRunnable");
      } else {
        throw new IllegalArgumentException("ohhh an Exception in MyRunnable");
      }
    }
  }
}

Вывод:

pool-1-thread-1, execution
I reschedule in case of Exception
pool-1-thread-1, execution
I reschedule in case of Exception
pool-1-thread-2, execution
I halt in case of Error
2
ответ дан 7 November 2019 в 09:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: