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

Как поймать исключения в 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, есть ли причина для этого?

4b9b3361

Ответ 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**();
..}
  1. Затем третий использует простой try catch внутри метода вызова, но вы не можете поймать исключение вне здесь.

  2. Обходной путь вызывает все методы вызова из метода вызова TaskFactory, фабрики, которая освобождает вызывающие вызовы.