Как поймать исключения в FutureTask
После обнаружения того, что FutureTask
работает в Executors.newCachedThreadPool()
на Java 1.6 (и из Eclipse), проглатывает исключения в методе Runnable.run()
, я попытался найти способ поймать их без добавления throw/catch для всех моих реализаций Runnable
.
API предполагает, что переопределение FutureTask.setException()
должно помочь в этом:
Заставляет это будущее сообщать об исключении Execution с заданным броском в качестве причины, если это будущее уже не было установлено или отменено. Этот метод вызывается внутренним методом run при сбое вычисления.
Однако этот метод, похоже, не вызван (работа с отладчиком показывает, что исключение попадает на FutureTask
, но setException
не вызывается). Я написал следующую программу для воспроизведения моей проблемы:
public class RunTest {
public static void main(String[] args) {
MyFutureTask t = new MyFutureTask(new Runnable() {
@Override
public void run() {
throw new RuntimeException("Unchecked exception");
}
});
ExecutorService service = Executors.newCachedThreadPool();
service.submit(t);
}
}
public class MyFutureTask extends FutureTask<Object> {
public MyFutureTask(Runnable r) {
super(r, null);
}
@Override
protected void setException(Throwable t) {
super.setException(t);
System.out.println("Exception: " + t);
}
}
Мой главный вопрос: как я могу поймать Исключения, брошенные в FutureTask? Почему вызов setException
не вызван?
Также мне хотелось бы знать, почему механизм Thread.UncaughtExceptionHandler
не используется FutureTask
, есть ли причина для этого?
Ответы
Ответ 1
setException
, вероятно, не предназначен для переопределения, но предоставляется, чтобы вы могли установить результат в исключение, если возникнет такая необходимость. Что вы хотите сделать, это переопределить метод done()
и попытаться получить результат:
public class MyFutureTask extends FutureTask<Object> {
public MyFutureTask(Runnable r) {
super(r, null);
}
@Override
protected void done() {
try {
if (!isCancelled()) get();
} catch (ExecutionException e) {
// Exception occurred, deal with it
System.out.println("Exception: " + e.getCause());
} catch (InterruptedException e) {
// Shouldn't happen, we're invoked when computation is finished
throw new AssertionError(e);
}
}
}
Ответ 2
Вы пробовали использовать UncaughtExceptionHandler
?
- Вам необходимо реализовать интерфейс
UncaughtExceptionHandler
.
- Чтобы установить поток
UncaughtExceptionHandler
для пула, укажите ThreadFactory
в вызове Executor.newCachedThreadPool(ThreadFactory)
.
- Вы можете установить UncaughtExceptionHandler для созданного потока через
setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
Отправьте задачи с ExecutorService.execute
, потому что только исключения, отбрасываемые из задач, отправленных с помощью execute
, переходят к обработчику неперехваченных исключений. Для задач, представленных с ExecutorService.submit
, любое исключенное исключение считается частью возвращаемого значения задачи. Если задача, отправленная с отправлением, завершается с исключением, она возвращается при вызове Future.get
, завернутой в ExecutionException
Ответ 3
Лучшее решение:
Проверка завершения Java FutureTask
Когда вы вызываете futureTask.get()
для получения результата вычисления, он генерирует исключение (ExecutionException), если базовое Runnable
/Callable
выбрасывает исключение.
ExecutionException.getCause()
вернет исключение, которое выбрало Runnable
/Callable
.
Он также выдает другое исключение, если Runnable
/Callable
был отменен.
Ответ 4
Я посмотрел исходный код FutureTask
и не смог найти, где вызывается setException
.
Существует метод innerSetException
из FutureTask.Sync
(внутренний класс FutureTask
), который вызывается в случае, когда Throwable
выбрасывается методом run. Этот метод также вызывается в setException
.
Так что швы вроде джавадока неправильны (или очень трудно понять...).
Ответ 5
Существует три стандартных способа и один импровизированный способ. 1. используйте UncaughtExceptionHandler, установите UncaughtExceptionHandler для созданного потока как
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable ex) {..}}
* Но ограничение заключается в том, что он ловит исключение, созданное потоком, но в случае будущей задачи оно проглатывается. 2. используйте afterExecute
после создания пользовательского threadpoolexecutor с крючком, который был специально предоставлен для этой цели. Просматривая код ThreadpoolExecutor, через submit> execute (есть workQueue, workQueue.offer
) задачи добавляются в рабочую очередь
final void runWorker(Worker arg0) {
Thread arg1 = Thread.currentThread();
Runnable arg2 = arg0.firstTask;
..
while(arg2 != null || (arg2 = this.**getTask()**) != null) {
arg0.lock();
..
try {
this.beforeExecute(arg1, arg2);
Object arg4 = null;
try {
arg2.run();
} catch (RuntimeException arg27) {
..
} finally {
this.**afterExecute**(arg2, (Throwable)arg4);
}
}
getTask() {..
this.workQueue.**poll**();
..}
-
Затем третий использует простой try catch внутри метода вызова, но вы не можете поймать исключение вне здесь.
-
Обходной путь вызывает все методы вызова из метода вызова TaskFactory, фабрики, которая освобождает вызывающие вызовы.