2009-12-03 54 views
41

Tôi đã tình cờ gặp một vấn đề, có thể được tóm tắt như sau:Tại sao UncaughtExceptionHandler không được ExecutorService gọi?

Khi tôi tạo ra các chủ đề bằng tay (tức là bằng instantiating java.lang.Thread) các UncaughtExceptionHandler được gọi một cách thích hợp. Tuy nhiên, khi tôi sử dụng một số ExecutorService với số ThreadFactory, trình xử lý này được sử dụng. Tôi đã bỏ lỡ cái gì?

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."); 
} 
} 

tôi mong đợi: Ba lần so với thông báo "của router ..."

tôi nhận: Thông điệp một lần (kích hoạt bởi các chủ đề được tạo thủ).

Sao chép bằng Java 1.6 trên Windows 7 và Mac OS X 10.5.

+0

Có lẽ công việc này, đối với tôi đó là ok Nó không phải là một FixedThreadPool , nhưng SingleThreadPool ... nhưng bạn nhận được ý tưởng http://stackoverflow.com/a/44007121/8020889 –

Trả lời

35

Vì ngoại lệ không bị vô hiệu hóa.

Chủ đề mà ThreadFactory của bạn tạo ra không được cung cấp trực tiếp cho Runnable hoặc Callable của bạn. Thay vào đó, Runnable mà bạn nhận được là một lớp Worker bên trong, ví dụ xem ThreadPoolExecutor $ Worker. Hãy thử System.out.println() trên Runnable cho newThread trong ví dụ của bạn.

Công nhân này nắm bắt bất kỳ RuntimeExceptions nào từ công việc bạn đã gửi.

Bạn có thể nhận ngoại lệ theo phương thức ThreadPoolExecutor#afterExecute.

+10

Mặc dù câu trả lời này là chính xác, nhưng việc triển khai đúng cách sau khi thực thi là khó khăn. Xem [câu hỏi tràn này] (http://stackoverflow.com/questions/2248131/handling-exceptions-from-java-executorservice-tasks) để biết ví dụ về cách triển khai đúng giải pháp được đề xuất tại đây. –

+1

Đây là một bài viết về xử lý ngoại lệ trong mã luồng, bao gồm 'Tương lai' và' ExecutorService': http://literatejava.com/threading/silent-thread-death-unhandled-exceptions/ –

5

Tôi vừa duyệt qua các câu hỏi cũ và nghĩ rằng tôi có thể chia sẻ giải pháp mà tôi đã triển khai trong trường hợp nó giúp ai đó (hoặc tôi bị lỗi).

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

Có một chút giải pháp. Trong phương pháp chạy, bạn có thể bắt tất cả các ngoại lệ, và sau đó làm một cái gì đó như thế này

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

này sẽ "đăng ký" ngoại lệ hiện như ném vào uncoughtExceptionHandler của bạn hoặc đến defualt xử lý ngoại lệ uncought. Bạn luôn có thể trả lại các ngoại lệ bắt buộc cho nhân viên của hồ bơi.

20

Exceptions được ném bởi nhiệm vụ nộp cho ExecutorService#submit được quấn thành an ExcecutionException và được sửa lại theo phương thức Future.get(). Điều này là do người thực thi coi ngoại lệ là một phần của kết quả của nhiệm vụ.

Nếu bạn tuy nhiên gửi tác vụ qua phương thức execute() bắt nguồn từ giao diện Executor, thì UncaughtExceptionHandler sẽ được thông báo.

+3

Đây là một giải pháp đáng ngạc nhiên trong sạch và dễ dàng . Cảm ơn! –

+0

Đến nay là giải pháp đơn giản nhất. Thông tin thêm về hành vi đó trong câu trả lời SO này: https://stackoverflow.com/a/3986509/2874005 – Tom

+0

Trong các thử nghiệm của tôi, nếu «ExecutorService' đã bị tắt, và chuỗi nhân viên còn sống sót cuối cùng của nó gặp phải một ngoại lệ không bị bắt, nó dường như chấm dứt trước khi gọi trình xử lý. Tôi không thể nhìn thấy từ 'ThreadPoolExecutor' tại sao điều này sẽ được như vậy mặc dù. – erickson

15

Trích từ cuốn sách Java Concurrency in Practice (trang 163), hy vọng điều này sẽ giúp

Hơi gây nhầm lẫn, trường hợp ngoại lệ ném từ nhiệm vụ làm cho nó để xử lý ngoại lệ còn tự do duy nhất cho các nhiệm vụ nộp cùng với thực hiện; đối với các tác vụ được gửi khi gửi, bất kỳ ngoại lệ được ném nào, được chọn hay không, được coi là một phần của trạng thái trả lại của nhiệm vụ . Nếu một nhiệm vụ được đệ trình với việc đệ trình chấm dứt với một ngoại lệ, thì nó được gọi lại bởi Future.get, được bao bọc trong một ExecutionException.

Dưới đây là ví dụ:

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(); 
    } 

} 

tôi sử dụng thực hiện nộp nhiệm vụ và kết quả đầu ra giao diện điều khiển "trong xử lý ngoại lệ"

Các vấn đề liên quan