2009-12-03 5 views
41

я наткнулся на проблемы, которые могут быть суммированы следующим образом:Почему UncaughtExceptionHandler не вызван ExecutorService?

При создании нити вручную (т.е. путем инстанцировании java.lang.Thread) в UncaughtExceptionHandler называется соответствующим образом. Однако, когда я использую ExecutorService с ThreadFactory, обработчик оммитирован. Что я упустил?

public class ThreadStudy { 

private static final int THREAD_POOL_SIZE = 1; 

public static void main(String[] args) { 

    // create uncaught exception handler 

    final UncaughtExceptionHandler exceptionHandler = new UncaughtExceptionHandler() { 

     @Override 
     public void uncaughtException(Thread t, Throwable e) { 
      synchronized (this) { 
       System.err.println("Uncaught exception in thread '" + t.getName() + "': " + e.getMessage()); 
      } 
     } 
    }; 

    // create thread factory 

    ThreadFactory threadFactory = new ThreadFactory() { 

     @Override 
     public Thread newThread(Runnable r) { 
      // System.out.println("creating pooled thread"); 
      final Thread thread = new Thread(r); 
      thread.setUncaughtExceptionHandler(exceptionHandler); 
      return thread; 
     } 
    }; 

    // create Threadpool 

    ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE, threadFactory); 

    // create Runnable 

    Runnable runnable = new Runnable() { 

     @Override 
     public void run() { 
      // System.out.println("A runnable runs..."); 
      throw new RuntimeException("Error in Runnable"); 
     } 
    }; 

    // create Callable 

    Callable<Integer> callable = new Callable<Integer>() { 

     @Override 
     public Integer call() throws Exception { 
      // System.out.println("A callable runs..."); 
      throw new Exception("Error in Callable"); 
     } 
    }; 

    // a) submitting Runnable to threadpool 
    threadPool.submit(runnable); 

    // b) submit Callable to threadpool 
    threadPool.submit(callable); 

    // c) create a thread for runnable manually 
    final Thread thread_r = new Thread(runnable, "manually-created-thread"); 
    thread_r.setUncaughtExceptionHandler(exceptionHandler); 
    thread_r.start(); 

    threadPool.shutdown(); 
    System.out.println("Done."); 
} 
} 

Я ожидаю: Три раза сообщения «неперехваченное исключение ...»

я получаю: Сообщение раз (вызываемое вручную созданный поток).

Воспроизводится с помощью Java 1.6 на Windows 7 и Mac OS X 10.5.

+0

Может быть, эту работу, для меня это нормально, это не FixedThreadPool , но SingleThreadPool ... но вы получаете идею http://stackoverflow.com/a/44007121/8020889 –

ответ

35

Потому что исключение не исчезает.

Поток, который производит ваш ThreadFactory, напрямую не передается вашим Runnable или Callable. Вместо этого Runnable, который вы получаете, является внутренним классом Worker, например, см. ThreadPoolExecutor $ Worker. Попробуйте System.out.println() на Runnable, данный newThread в вашем примере.

Этот работник ловит любые RuntimeExceptions из предоставленной вами работы.

Вы можете получить исключение в методе ThreadPoolExecutor#afterExecute.

+10

Хотя этот ответ правильный, правильная реализация afterExecute сложна. См. [Этот вопрос о переполнении] (http://stackoverflow.com/questions/2248131/handling-exceptions-from-java-executorservice-tasks) для примера того, как правильно реализовать предлагаемое здесь решение. –

+1

Вот статья об обработке исключений в потоковом коде, включая 'Future' и' ExecutorService': http://literatejava.com/threading/silent-thread-death-unhandled-exceptions/ –

5

Я только что просмотрел мои старые вопросы и подумал, что могу поделиться решением, которое я реализовал, в случае, если он помогает кому-то (или я пропустил ошибку).

import java.lang.Thread.UncaughtExceptionHandler; 
import java.util.concurrent.Callable; 
import java.util.concurrent.Delayed; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.FutureTask; 
import java.util.concurrent.RunnableScheduledFuture; 
import java.util.concurrent.ScheduledThreadPoolExecutor; 
import java.util.concurrent.ThreadFactory; 
import java.util.concurrent.TimeUnit; 


/** 
* @author Mike Herzog, 2009 
*/ 
public class ExceptionHandlingExecuterService extends ScheduledThreadPoolExecutor { 

    /** My ExceptionHandler */ 
    private final UncaughtExceptionHandler exceptionHandler; 

    /** 
    * Encapsulating a task and enable exception handling. 
    * <p> 
    * <i>NB:</i> We need this since {@link ExecutorService}s ignore the 
    * {@link UncaughtExceptionHandler} of the {@link ThreadFactory}. 
    * 
    * @param <V> The result type returned by this FutureTask's get method. 
    */ 
    private class ExceptionHandlingFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> { 

     /** Encapsulated Task */ 
     private final RunnableScheduledFuture<V> task; 

     /** 
     * Encapsulate a {@link Callable}. 
     * 
     * @param callable 
     * @param task 
     */ 
     public ExceptionHandlingFutureTask(Callable<V> callable, RunnableScheduledFuture<V> task) { 
      super(callable); 
      this.task = task; 
     } 

     /** 
     * Encapsulate a {@link Runnable}. 
     * 
     * @param runnable 
     * @param result 
     * @param task 
     */ 
     public ExceptionHandlingFutureTask(Runnable runnable, RunnableScheduledFuture<V> task) { 
      super(runnable, null); 
      this.task = task; 
     } 

     /* 
     * (non-Javadoc) 
     * @see java.util.concurrent.FutureTask#done() The actual exception 
     * handling magic. 
     */ 
     @Override 
     protected void done() { 
      // super.done(); // does nothing 
      try { 
       get(); 

      } catch (ExecutionException e) { 
       if (exceptionHandler != null) { 
        exceptionHandler.uncaughtException(null, e.getCause()); 
       } 

      } catch (Exception e) { 
       // never mind cancelation or interruption... 
      } 
     } 

     @Override 
     public boolean isPeriodic() { 
      return this.task.isPeriodic(); 
     } 

     @Override 
     public long getDelay(TimeUnit unit) { 
      return task.getDelay(unit); 
     } 

     @Override 
     public int compareTo(Delayed other) { 
      return task.compareTo(other); 
     } 

    } 

    /** 
    * @param corePoolSize The number of threads to keep in the pool, even if 
    *  they are idle. 
    * @param eh Receiver for unhandled exceptions. <i>NB:</i> The thread 
    *  reference will always be <code>null</code>. 
    */ 
    public ExceptionHandlingExecuterService(int corePoolSize, UncaughtExceptionHandler eh) { 
     super(corePoolSize); 
     this.exceptionHandler = eh; 
    } 

    @Override 
    protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) { 
     return new ExceptionHandlingFutureTask<V>(callable, task); 
    } 

    @Override 
    protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) { 
     return new ExceptionHandlingFutureTask<V>(runnable, task); 
    } 
} 
2

Существует небольшое обходное решение. В вашем методе запуска, вы можете поймать каждое исключение, а затем сделать что-то вроде этого

Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), ex); 
//or 
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), ex); 

это будет «зарегистрировать» текущее исключение, как брошенный на ваш uncoughtExceptionHandler или к DEFUALT uncought обработчика исключений. Вы всегда можете перехватывать исключения для рабочего пула.

20

Исключения, которые брошенные задачами, представленных ExecutorService#submit получить завернутую в ExcecutionException и возвращаются методом Future.get(). Это происходит потому, что исполнитель рассматривает исключение как часть результата задачи.

Если вы отправляете задание с помощью метода execute(), который исходит из интерфейса Executor, уведомляется UncaughtExceptionHandler.

+3

Это удивительно чистое и простое решение , Благодаря! –

+0

Самым простым решением. Дополнительная информация об этом поведении в этом ответе SO: https://stackoverflow.com/a/3986509/2874005 – Tom

+0

В моих экспериментах, если «ExecutorService» был остановлен, а его последний оставшийся рабочий поток встречает неперехваченное исключение, кажется, заканчивается перед вызовом обработчика. Я не вижу из «ThreadPoolExecutor», почему это было бы так. – erickson

15

Цитата из книги Java Параллелизма на практике (стр 163), надеюсь, что это помогает

Несколько смутно, исключения выброшенных из задач сделать его неперехваченный обработчик исключения только для задач, представленных с казнить; для поставленных задач с отправкой, любое исключенное исключение, проверенное или нет, считается частью статуса возврата задачи . Если задание, отправленное с отправлением, завершается с исключением, , оно заново завершено Future.get, завернутым в ExecutionException.

Вот пример:

public class Main { 

public static void main(String[] args){ 


    ThreadFactory factory = new ThreadFactory(){ 

     @Override 
     public Thread newThread(Runnable r) { 
      // TODO Auto-generated method stub 
      final Thread thread =new Thread(r); 

      thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 

       @Override 
       public void uncaughtException(Thread t, Throwable e) { 
        // TODO Auto-generated method stub 
        System.out.println("in exception handler"); 
       } 
      }); 

      return thread; 
     } 

    }; 

    ExecutorService pool=Executors.newSingleThreadExecutor(factory); 
    pool.execute(new testTask()); 

} 



private static class testTask implements Runnable { 

    @Override 
    public void run() { 
     // TODO Auto-generated method stub 
     throw new RuntimeException(); 
    } 

} 

Я использую выполнить, чтобы представить задачу и выходов пульта «в обработчик исключений»

Смежные вопросы