Как правильно улавливать RuntimeExceptions от исполнителей?
Скажем, что у меня есть следующий код:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
Теперь, если myRunnable
выбрасывает RuntimeExcpetion
, как я могу его поймать? Одним из способов было бы реализовать мою собственную реализацию ThreadFactory
до newSingleThreadExecutor()
и установить пользовательский uncaughtExceptionHandler
для Thread
, который выйдет из него. Другой способ - обернуть myRunnable
локальному (анонимному) Runnable
, содержащему try-catch -block. Возможно, есть и другие подобные обходные пути. Но... как-то это кажется грязным, я чувствую, что это не должно быть так сложно. Есть ли чистое решение?
Ответы
Ответ 1
Чистым решением является использование 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();
}
Ответ 2
Почему бы не вызвать ExecutorService#submit()
, получить Future
назад, а затем обрабатывать возможные исключения самостоятельно при вызове Future#get()
?
Ответ 3
Украсьте 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));
Ответ 4
skaffman правилен тем, что использование submit
- самый чистый подход. Альтернативный подход заключается в подклассе ThreadPoolExecutor
и переопределить afterExecute(Runnable, Throwable)
. Если вы выполните этот подход , не вызывайте execute(Runnable)
, а не submit(Runnable)
или afterExecute
.
В описании API:
Метод, вызванный по завершении выполнение данного Runnable. Эта метод вызывается потоком, который выполнил задачу. Если непустой, Хищение - это RuntimeException
или Error
, что вызвало выполнение прерывается внезапно.
Примечание. Когда действия заключены в задач (таких как FutureTask) либо явно или с помощью таких методов, как Отправить, эти объекты задачи поймать и поддерживать вычислительные исключения и поэтому они не вызывают резких прерывания и внутреннего исключения не передаются Метод.
Ответ 5
задача (Callable
или Runnable
), отправленная в ThreadPoolExecutors
, будет преобразована в FuturnTask
, содержит ссылку с именем Callable
, равную задаче, которую вы отправляете. FuturnTask имеет свой собственный метод run
следующим образом. Все исключения или броски, выброшенные в c.call()
, будут выхвачены и помещены в прозвище с именем outcome
. При вызове метода FuturnTask get
outcome
будет выбрано
FuturnTask.run Исходный код 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
}
}