Как ловить исключения в FutureTask - PullRequest
20 голосов
/ 24 августа 2010

Обнаружив, что FutureTask, работающий в Executors.newCachedThreadPool() на Java 1.6 (и из Eclipse), проглатывает исключения в методе Runnable.run(), я попытался придумать способ отловить их, не добавляя throw / catchко всем моим Runnable реализациям.

API предлагает, чтобы переопределение FutureTask.setException() должно помочь в этом:

Заставляет это будущее сообщать об исключении ExecutionException с заданным throwable в качестве причины, если это Будущее уже не было установлено или было отменено.Этот метод вызывается изнутри методом 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, есть ли для этого какая-то причина?

Ответы [ 5 ]

20 голосов
/ 24 августа 2010

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);
        }
    }
}
12 голосов
/ 24 августа 2010

Вы пытались использовать UncaughtExceptionHandler?

  • Вам необходимо реализовать интерфейс UncaughtExceptionHandler.
  • Дляустановите UncaughtExceptionHandler для потоков пула, укажите ThreadFactory в вызове Executor.newCachedThreadPool(ThreadFactory).
  • Вы можете установить UncaughtExceptionHandler для созданного потока с помощью setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

Отправлять задачи с помощью ExecutorService.execute, потому что только исключения выбрасываются из задач, отправленных сexecute сделать это обработчику необработанных исключений.Для задач, переданных с ExecutorService.submit, любое выброшенное исключение считается частью возвращаемого значения задачи.Если задача, отправленная с помощью submit, завершается с исключением, она перебрасывается при вызове Future.get, заключенная в ExecutionException

9 голосов
/ 31 марта 2013

Гораздо лучшее решение: Проверка завершения Java FutureTask

Когда вы вызываете futureTask.get() для получения результата вычисления, он выдаст исключение (ExecutionException), если базовый Runnable / Callable бросил исключение.

ExecutionException.getCause() вернет исключение, которое бросил Runnable / Callable.

Также будет выдано другое исключение, если Runnable / Callable был отменен.

2 голосов
/ 24 августа 2010

Я посмотрел на исходный код FutureTask и не смог найти, где вызывается setException.
Существует метод innerSetException из FutureTask.Sync (внутренний класс FutureTask), которыйвызывается в случае выброса Throwable методом run.Этот метод также вызывается в setException.
, поэтому он выглядит так, как если бы Javadoc был неправильным (или очень трудным для понимания ...).

0 голосов
/ 23 мая 2018

Существует три стандартных пути и один импровизированный.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**();
..}

Затем третий использует простую попытку catch внутри метода вызова, но вы не можете перехватить исключение снаружи.

Временное решение вызывает все методы вызова изметод вызова TaskFactory, фабрики, которая выпускает вызываемые объекты.

...