ScheduledExecutorService Обработка исключений - PullRequest
45 голосов
/ 01 августа 2011

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

p-код:

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()?Или любой встроенный метод обратного вызова для обработки исключения?Спасибо!

Ответы [ 6 ]

81 голосов
/ 23 июля 2014

tl; dr

Любое исключение, выходящее из вашего метода run, останавливает всю дальнейшую работу без уведомления.

Всегда используйте try-catch в вашем методе run.Попытайтесь восстановить, если вы хотите, чтобы запланированные действия продолжались.

@Override
public void run ()
{
    try {
        doChore();
    } catch ( Exception e ) { 
        logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() );
    }
}

Проблема

Этот вопрос относится к критической уловке с ScheduledExecutorService: Любое выброшенное исключение или ошибка, достигающая исполнителя, приводит к его остановке. Больше никаких вызовов в Runnable, больше не выполняется работа.Эта остановка работы происходит тихо, вы не будете проинформированы.Это непослушное сообщение в блоге занимательно рассказывает о сложном способе узнать об этом поведении.

Решение

Ответ от yegor256 и ответ от arun_suresh оба кажутся в основном правильными.Две проблемы с этими ответами:

  • Ошибки перехвата, а также исключения
  • Немного сложно

Ошибки и Исключения?

В Java мы обычно ловим только исключений , а не ошибок .Но в этом особом случае ScheduledExecutorService отказ от перехвата будет означать остановку работы.Таким образом, вы можете поймать оба.Я не уверен на 100% в этом, не зная полностью последствий всех ошибок.Пожалуйста, исправьте меня, если необходимо.

Один из способов отловить как исключения, так и ошибки - перехватить их суперкласс, Throwable .

} catch ( Throwable t ) {

… вместо…

} catch ( Exception e ) {

Простейший подход: просто добавьте Try-Catch

Но оба ответа немного сложны.Просто для справки, я покажу самое простое решение:

Всегда оборачивайте код вашего Runnable в Try-Catch, чтобы отловить все исключения и .

Лямбда-синтаксис

С лямбдой (в Java 8 и более поздних версиях).

final Runnable someChoreRunnable = () -> {
    try {
        doChore();
    } catch ( Throwable t ) {  // Catch Throwable rather than Exception (a subclass).
        logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() );
    }
};

Старомодный синтаксис

Старый-по-старому, до лямбды.

final Runnable someChoreRunnable = new Runnable()
{
    @Override
    public void run ()
    {
        try {
            doChore();
        } catch ( Throwable t ) {  // Catch Throwable rather than Exception (a subclass).
            logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() );
        }
    }
};

В каждом Runnable / Callable

Независимо от ScheduledExecutorService, мне кажется разумным всегда использовать общий try-catch( Exception† e ) in any run метод Runnable.То же самое для любого call метода Callable.


Полный пример кода

В реальной работе я бы, вероятно,определить Runnable отдельно, а не вложенно.Но это делает для аккуратного примера все-в-одном.

package com.basilbourque.example;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 *  Demo `ScheduledExecutorService`
 */
public class App {
    public static void main ( String[] args ) {
        App app = new App();
        app.doIt();
    }

    private void doIt () {

        // Demonstrate a working scheduled executor service.
        // Run, and watch the console for 20 seconds.
        System.out.println( "BASIL - Start." );

        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        ScheduledFuture < ? > handle =
                scheduler.scheduleWithFixedDelay( new Runnable() {
                    public void run () {
                        try {
                            // doChore ;   // Do business logic.
                            System.out.println( "Now: " + ZonedDateTime.now( ZoneId.systemDefault() ) );  // Report current moment.
                        } catch ( Exception e ) {
                            // … handle exception/error. Trap any unexpected exception here rather to stop it reaching and shutting-down the scheduled executor service.
                            // logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + e.getStackTrace() );
                        }   // End of try-catch.
                    }   // End of `run` method.
                } , 0 , 2 , TimeUnit.SECONDS );


        // Wait a long moment, for background thread to do some work.
        try {
            Thread.sleep( TimeUnit.SECONDS.toMillis( 20 ) );
        } catch ( InterruptedException e ) {
            e.printStackTrace();
        }

        // Time is up. Kill the executor service and its thread pool.
        scheduler.shutdown();

        System.out.println( "BASIL - Done." );

    }
}

При запуске.

BASIL - Start.

Сейчас: 2018-04-10T16: 46: 01.423286-07: 00 [America / Los_Angeles]

Сейчас: 2018-04-10T16: 46: 03.449178-07: 00 [America / Los_Angeles]

Сейчас: 2018-04-10T16: 46: 05.450107-07: 00 [Америка / Los_Angeles]

Сейчас: 2018-04-10T16: 46: 07.450586-07: 00 [America / Los_Angeles]

Сейчас: 2018-04-10T16: 46: 09.456076-07: 00 [America / Los_Angeles]

Сейчас: 2018-04-10T16: 46: 11.456872-07: 00 [America / Los_Angeles]

Сейчас: 2018-04-10T16: 46: 13.461944-07: 00 [America / Los_Angeles]

Сейчас: 2018-04-10T16: 46: 15.463837-07: 00 [America / Los_Angeles]

Сейчас: 2018-04-10T16: 46: 17.469218-07: 00 [America / Los_Angeles]

Сейчас: 2018-04-10T16: 46: 19.473935-07: 00 [America / Los_Angeles]

BASIL - Готово.


† Или, возможно, Throwable вместо Exception, чтобы поймать Error объекты тоже.

32 голосов
/ 01 августа 2011

Вы должны использовать объект ScheduledFuture, возвращаемый вашим scheduler.scheduleWithFixedDelay(...), например:

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> handle =
        scheduler.scheduleWithFixedDelay(new Runnable() {
             public void run() { 
                 throw new RuntimeException("foo");
             }
        }, 1, 10, TimeUnit.SECONDS);

// Create and Start an exception handler thread
// pass the "handle" object to the thread
// Inside the handler thread do :
....
try {
  handle.get();
} catch (ExecutionException e) {
  Exception rootException = e.getCause();
}
3 голосов
/ 14 июня 2018

Вдохновленный решением @MBec, я написал хорошую универсальную оболочку для ScheduledExecutorService, которая:

  • будет перехватывать и распечатывать любое необработанное исключение.
  • вернет Java 8 CompletableFutureвместо будущего.

:)

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * This class use as a wrapper for the Native Java ScheduledExecutorService class.
 * It was created in order to address the very unpleasant scenario of silent death!
 * explanation: each time an unhandled exception get thrown from a running task that runs by ScheduledExecutorService
 * the thread will die and the exception will die with it (nothing will propagate back to the main thread).
 *
 * However, HonestScheduledExecutorService will gracefully print the thrown exception with a custom/default message,
 * and will also return a Java 8 compliant CompletableFuture for your convenience :)
 */
@Slf4j
public class HonestScheduledExecutorService {

    private final ScheduledExecutorService scheduledExecutorService;
    private static final String DEFAULT_FAILURE_MSG = "Failure occurred when running scheduled task.";

    HonestScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
        this.scheduledExecutorService = scheduledExecutorService;
    }

    public CompletableFuture<Object> scheduleWithFixedDelay(Callable callable, String onFailureMsg, long initialDelay, long delay, TimeUnit unit) {
        final String msg = StringUtils.isEmpty(onFailureMsg) ? DEFAULT_FAILURE_MSG : onFailureMsg;
        CompletableFuture<Object> delayed = new CompletableFuture<>();

        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            try {
                Object result = callable.call();
                delayed.complete(result);
            } catch (Throwable th) {
                log.error(msg, th);
                delayed.completeExceptionally(th);
            }
        }, initialDelay, delay, unit);

        return delayed;
    }

    public CompletableFuture<Void> scheduleWithFixedDelay(Runnable runnable, String onFailureMsg, long initialDelay, long delay, TimeUnit unit) {
        final String msg = StringUtils.isEmpty(onFailureMsg) ? DEFAULT_FAILURE_MSG : onFailureMsg;
        CompletableFuture<Void> delayed = new CompletableFuture<>();

        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            try {
                runnable.run();
                delayed.complete(null);
            } catch (Throwable th) {
                log.error(msg, th);
                delayed.completeExceptionally(th);
            }
        }, initialDelay, delay, unit);

        return delayed;
    }

    public CompletableFuture<Object> schedule(Callable callable, String failureMsg, long delay, TimeUnit unit) {
        final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
        CompletableFuture<Object> delayed = new CompletableFuture<>();

        scheduledExecutorService.schedule(() -> {
            try {
                Object result = callable.call();
                delayed.complete(result);
            } catch (Throwable th) {
                log.error(msg, th);
                delayed.completeExceptionally(th);
            }
        }, delay, unit);

        return delayed;
    }

    public CompletableFuture<Void> schedule(Runnable runnable, String failureMsg, long delay, TimeUnit unit) {
        final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
        CompletableFuture<Void> delayed = new CompletableFuture<>();

        scheduledExecutorService.schedule(() -> {
            try {
                runnable.run();
                delayed.complete(null);
            } catch (Throwable th) {
                log.error(msg, th);
                delayed.completeExceptionally(th);
            }
        }, delay, unit);

        return delayed;
    }

    public CompletableFuture<Object> scheduleAtFixedRate(Callable callable, String failureMsg, long initialDelay, long period, TimeUnit unit) {
        final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
        CompletableFuture<Object> delayed = new CompletableFuture<>();

        scheduledExecutorService.scheduleAtFixedRate(() -> {
            try {
                Object result = callable.call();
                delayed.complete(result);
            } catch (Throwable th) {
                log.error(msg, th);
                delayed.completeExceptionally(th);
            }
        }, initialDelay, period, unit);

        return delayed;
    }

    public CompletableFuture<Void> scheduleAtFixedRate(Runnable runnable, String failureMsg, long initialDelay, long period, TimeUnit unit) {
        final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
        CompletableFuture<Void> delayed = new CompletableFuture<>();

        scheduledExecutorService.scheduleAtFixedRate(() -> {
            try {
                runnable.run();
                delayed.complete(null);
            } catch (Throwable th) {
                log.error(msg, th);
                delayed.completeExceptionally(th);
            }
        }, initialDelay, period, unit);

        return delayed;
    }

    public CompletableFuture<Object> execute(Callable callable, String failureMsg) {
        final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
        CompletableFuture<Object> delayed = new CompletableFuture<>();

        scheduledExecutorService.execute(() -> {
            try {
                Object result = callable.call();
                delayed.complete(result);
            } catch (Throwable th) {
                log.error(msg, th);
                delayed.completeExceptionally(th);
            }
        });

        return delayed;
    }

    public CompletableFuture<Void> execute(Runnable runnable, String failureMsg) {
        final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg;
        CompletableFuture<Void> delayed = new CompletableFuture<>();

        scheduledExecutorService.execute(() -> {
            try {
                runnable.run();
                delayed.complete(null);
            } catch (Throwable th) {
                log.error(msg, th);
                delayed.completeExceptionally(th);
            }
        });

        return delayed;
    }

    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return scheduledExecutorService.awaitTermination(timeout, unit);
    }

    public List<Runnable> shutdownNow() {
        return scheduledExecutorService.shutdownNow();
    }

    public void shutdown() {
        scheduledExecutorService.shutdown();
    }

}
3 голосов
/ 30 октября 2017

Я знаю, что это старый вопрос, но если кто-то использует задержанный CompletableFuture с ScheduledExecutorService, то должен обработать это следующим образом:

private static CompletableFuture<String> delayed(Duration delay) {
    CompletableFuture<String> delayed = new CompletableFuture<>();
    executor.schedule(() -> {
        String value = null;
        try {
            value = mayThrowExceptionOrValue();
        } catch (Throwable ex) {
            delayed.completeExceptionally(ex);
        }
        if (!delayed.isCompletedExceptionally()) {
            delayed.complete(value);
        }
    }, delay.toMillis(), TimeUnit.MILLISECONDS);
    return delayed;
}

и обработать исключение в CompletableFuture:

CompletableFuture<String> delayed = delayed(Duration.ofSeconds(5));
delayed.exceptionally(ex -> {
    //handle exception
    return null;
}).thenAccept(value -> {
    //handle value
});
3 голосов
/ 10 мая 2012

Другим решением было бы проглотить исключение в Runnable.Вы можете использовать удобный VerboseRunnable класс из jcabi-log , например:

import com.jcabi.log.VerboseRunnable;
scheduler.scheduleWithFixedDelay(
  new VerboseRunnable(
    Runnable() {
      public void run() { 
        // do business logic, may Exception occurs
      }
    },
    true // it means that all exceptions will be swallowed and logged
  ),
  1, 10, TimeUnit.SECONDS
);
0 голосов
/ 10 января 2018

Любое исключение в run () потока, который передается (ScheduledExecutorService), никогда не выбрасывается, и если мы используем future.get () для получения статуса, то основной поток ждет бесконечно

...