Обрабатывание исключений от задач Java ExecutorService

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

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

Например:

public class ThreadPoolErrors extends ThreadPoolExecutor {
    public ThreadPoolErrors() {
        super(  1, // core threads
                1, // max threads
                1, // timeout
                TimeUnit.MINUTES, // timeout units
                new LinkedBlockingQueue<Runnable>() // work queue
        );
    }

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if(t != null) {
            System.out.println("Got an error: " + t);
        } else {
            System.out.println("Everything's fine--situation normal!");
        }
    }

    public static void main( String [] args) {
        ThreadPoolErrors threadPool = new ThreadPoolErrors();
        threadPool.submit( 
                new Runnable() {
                    public void run() {
                        throw new RuntimeException("Ouch! Got an error.");
                    }
                }
        );
        threadPool.shutdown();
    }
}

Вывод из этой программы, "Все прекрасно - нормальная ситуация!" даже при том, что единственное Выполнимое, отправленное пулу потоков, выдает исключение. Какой-либо ключ к разгадке того, что продолжается здесь?

Спасибо!

201
задан Andrii Abramov 20 June 2017 в 05:01
поделиться

4 ответа

Из документов :

Примечание: когда действия заключены в задачи (например, FutureTask) либо явно, либо с помощью таких методов, как {{1} } submit, эти объекты задач улавливают и поддерживают вычислительные исключения и , чтобы они не вызывали резкого завершения, а внутренние исключения не передаются этому метод.

Когда вы отправляете Runnable, он переносится в Future.

Ваш afterExecute должен выглядеть примерно так:

public final class ExtendedExecutor extends ThreadPoolExecutor {

    // ...

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t == null && r instanceof Future<?>) {
            try {
                Future<?> future = (Future<?>) r;
                if (future.isDone()) {
                    future.get();
                }
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        if (t != null) {
            System.out.println(t);
        }
    }
}
149
ответ дан 23 November 2019 в 05:02
поделиться

Объяснение этого поведения прямо в javadoc для afterExecute :

Примечание. Когда действия заключены в задачи (например, FutureTask), либо явно или с помощью таких методов, как submit, эти объекты задач улавливают и поддерживают вычислительные исключения, а также , чтобы они не вызывали резкого завершения , а внутренние исключения не передаются этому методу .

17
ответ дан 23 November 2019 в 05:02
поделиться

ПРЕДУПРЕЖДЕНИЕ : следует отметить, что это решение блокирует вызывающий поток.


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

Callable.call () может генерировать проверенные исключения, и они передаются обратно в вызывающий поток:

Callable task = ...
Future future = executor.submit(task);
try {
   future.get();
} catch (ExecutionException ex) {
   ex.getCause().printStackTrace();
}

Если Callable.call () генерирует исключение, это будет завернутый в ExecutionException и выданный Future.get () .

Это, вероятно, будет намного предпочтительнее, чем создание подкласса ThreadPoolExecutor . Это также дает вам возможность повторно отправить задачу, если исключение подлежит исправлению.

241
ответ дан 23 November 2019 в 05:02
поделиться

Вместо создания подкласса ThreadPoolExecutor я бы предоставил ему экземпляр ThreadFactory , который создает новые потоки и предоставляет им UncaughtExceptionHandler

-4
ответ дан 23 November 2019 в 05:02
поделиться
Другие вопросы по тегам:

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