Как правильно перехватывать исключения RuntimeExceptions от Исполнителей?
-
18-09-2019 - |
Вопрос
Скажите, что у меня есть следующий код:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
Теперь, если myRunnable
бросает RuntimeExcpetion
, как я могу это поймать?Одним из способов было бы снабдить меня своим собственным ThreadFactory
внедрение для newSingleThreadExecutor()
и установите пользовательский uncaughtExceptionHandler
s для Thread
то, что из этого получается.Другим способом было бы обернуть myRunnable
местному жителю (анонимному) Runnable
который содержит блок try-catch -block.Возможно, существуют и другие подобные обходные пути.Но...почему-то это кажется грязным, я чувствую, что это не должно быть так сложно.Есть ли чистое решение?
Решение
Чистый обходной путь заключается в использовании ExecutorService.submit()
вместо того , чтобы execute()
.Это возвращает вам Future
который вы можете использовать для извлечения результата или исключения из задачи:
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable task = new Runnable() {
public void run() {
throw new RuntimeException("foo");
}
};
Future<?> future = executor.submit(task);
try {
future.get();
} catch (ExecutionException e) {
Exception rootException = e.getCause();
}
Другие советы
Украсьте runnable в другом runnable, который улавливает исключения во время выполнения и обрабатывает их:
public class REHandler implements Runnable {
Runnable delegate;
public REHandler (Runnable delegate) {
this.delegate = delegate;
}
public void run () {
try {
delegate.run ();
} catch (RuntimeException e) {
... your fancy error handling here ...
}
}
}
executor.execute(new REHandler (myRunnable));
Почему бы не позвонить ExecutorService#submit()
, получить Future
вернуться, а затем самостоятельно обрабатывать возможные исключения при вызове Future#get()
?
скаффман прав в том , что использует submit
это самый чистый подход.Альтернативный подход заключается в создании подкласса ThreadPoolExecutor
и переопределить afterExecute(Runnable, Throwable)
.Если вы будете следовать этому подходу обязательно позвоните execute(Runnable)
вместо того , чтобы submit(Runnable)
или afterExecute
не будет вызван.
Согласно описанию API:
Метод, вызываемый по завершении выполнения данного Runnable.Этот метод вызывается потоком, который выполнил задачу.Если значение не равно нулю, то Выбрасываемый является неперехваченным
RuntimeException
илиError
это привело к внезапному завершению выполнения.Примечание:Когда действия заключены в задачи (такие как FutureTask) либо явно или с помощью таких методов, как submit, эти объекты задачи перехватывают и поддерживают вычислительные исключения, и таким образом, они не вызывают внезапного завершения и внутренних исключений являются не передано этому метод.
задача(Callable
или Runnable
) представлен в ThreadPoolExecutors
будет преобразован в FuturnTask
, содержит реквизит с именем callable
равно заданию, которое вы отправляете.FuturnTask имеет свои собственные run
способ следующий.Все исключения или выбрасываемые, добавленные в c.call()
будет пойман и помещен в реквизит под названием outcome
.При вызове FuturnTask get
способ, outcome
будет выброшен
FuturnTask.запуск Из исходного кода Jdk1.8
public void run() {
...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// save ex into `outcome` prop
setException(ex);
}
if (ran)
set(result);
}
}
...
}
если вы хотите перехватить исключение :
- 1.ответ скаффмана
- 2.перезапишите `afterExecute` при создании нового ThreadPoolExecutor
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Throwable cause = null;
if (t == null && r instanceof Future) {
try {
((Future<?>) r).get();
} catch (InterruptedException | ExecutionException e) {
cause = e;
}
} else if (t != null) {
cause = t;
}
if (cause != null) {
// log error
}
}