Поток способен обрабатывать только один Runable в целом. При выходе из метода Thread.run () поток умирает. ThreadPoolExecutor реализует трюк, чтобы заставить поток обрабатывать несколько Runnables: он использует собственную реализацию Runnable. Потоки запускаются с реализацией Runnable, которая выбирает другие Runanbles (ваши Runnables) из ExecutorService и выполняет их: ThreadPoolExecutor -> Thread -> Worker -> YourRunnable. Когда в вашей реализации Runnable возникает неперехваченное исключение, оно попадает в блок finally Worker.run (). В этом окончательном блоке класс Worker сообщает ThreadPoolExecutor, что он «завершил» работу. Исключение еще не поступило в класс Thread, но ThreadPoolExecutor уже зарегистрировал работника как бездействующего.
И вот тут начинается самое интересное. Метод awaitTermination () будет вызван, когда все Runnables будут переданы Исполнителю. Это происходит очень быстро, так что, вероятно, ни один из Runnables не закончил свою работу. Работник переключится в режим ожидания, если возникнет исключение, прежде чем исключение достигнет класса Thread. Если ситуация аналогична для других потоков (или если они закончили свою работу), все работники сигнализируют «бездействует» и возвращает awaitTermination (). Основной поток достигает строки кода, где он проверяет размер собранного списка исключений. И это может произойти до того, как какой-либо (или какой-либо) из потоков сможет вызвать UncaughtExceptionHandler. Это зависит от порядка выполнения, если или сколько исключений будет добавлено в список необработанных исключений до того, как основной поток его прочитает.
Очень неожиданное поведение. Но я не оставлю тебя без рабочего решения. Итак, давайте заставим это работать.
Нам повезло, что класс ThreadPoolExecutor был разработан для расширяемости. Существует пустой защищенный метод afterExecute (Runnable r, Throwable t). Это будет вызвано непосредственно после метода run () нашего Runnable до того, как работник сообщит, что он завершил работу. Правильным решением является расширение ThreadPoolExecutor для обработки необработанных исключений:
public class ExceptionAwareThreadPoolExecutor extends ThreadPoolExecutor {
private final List<Throwable> uncaughtExceptions =
Collections.synchronizedList(new LinkedList<Throwable>());
@Override
protected void afterExecute(final Runnable r, final Throwable t) {
if (t != null) uncaughtExceptions.add(t);
}
public List<Throwable> getUncaughtExceptions() {
return Collections.unmodifiableList(uncaughtExceptions);
}
}