Подтвердить что ты не робот

Как правильно улавливать RuntimeExceptions от исполнителей?

Скажем, что у меня есть следующий код:

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);

Теперь, если myRunnable выбрасывает RuntimeExcpetion, как я могу его поймать? Одним из способов было бы реализовать мою собственную реализацию ThreadFactory до newSingleThreadExecutor() и установить пользовательский uncaughtExceptionHandler для Thread, который выйдет из него. Другой способ - обернуть myRunnable локальному (анонимному) Runnable, содержащему try-catch -block. Возможно, есть и другие подобные обходные пути. Но... как-то это кажется грязным, я чувствую, что это не должно быть так сложно. Есть ли чистое решение?

4b9b3361

Ответ 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. 1. Скаффман ответ
  
  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
            }
        }