Как правильно поймать RuntimeExceptions от исполнителей?
сказать, что у меня есть следующий код:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
теперь, если myRunnable бросает!--2-->, Как я могу поймать его? Одним из способов было бы поставить мой собственный ThreadFactory реализации newSingleThreadExecutor() и настройки uncaughtExceptionHandlerС Threads, которые выходят из него. Другой способ-обернуть myRunnable для местного (анонимного) Runnable который содержит блок try-catch. Возможно,есть и другие подобные обходные пути. Но... почему-то это кажется грязным, я чувствую, что это не должно быть так сложно. Есть есть ли чистое решение?
5 ответов
чистый обходной путь должен использовать 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() ?
skaffman правильно, что с помощью submit самый чистый подход.  Альтернативный подход-подкласс ThreadPoolExecutor и заменить afterExecute(Runnable, Throwable).  Если вы будете следовать этому подходу обязательно позвоните execute(Runnable), а не submit(Runnable) или afterExecute не будет вызван.
согласно описанию API:
метод вызывается по завершении исполнение данного Runnable. Этот метод вызывается потоком, который исполнено задача. Если значение не равно null, Throwable-это не пойманный
RuntimeExceptionилиErrorчто вызвало казнь резко прекратится.Примечание: когда действия заключены в задачи (например, FutureTask) либо явно или с помощью таких методов, как submit, эти объекты задачи поймать и поддерживать вычислительные исключения и поэтому они не вызывают резкого прекращение, и внутреннее исключение составляют не передано этому метод.
задача(Callable или Runnable) представлен ThreadPoolExecutors будет преобразовано в FuturnTask, содержит опору с именем callable равно задаче, которую вы отправляете. FuturnTask имеет свой собственный run метод следующим образом. Все исключения или throwable throwed in 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. skaffman ответ
-  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
            }
        }
