聲明: 捕獲異步線程異常的方式有很多,本文只介紹(並示例)幾種相對常用的方式。
捕獲異步線程異常的常用方式(結論):
開啓線程的方式 | 使用線程池的情況 | 捕獲異步線程異常的常用方式 |
---|---|---|
Thread | 不使用線程池 |
1. Thread.setDefaultUncaughtExceptionHandler 設置默認的線程異常處理器。2. Thread實例.setUncaughtExceptionHandler 給當指定線程設置線程異常處理器。 |
Thread | 使用線程池ExecutorService#execute |
1. Thread.setDefaultUncaughtExceptionHandler 設置默認的線程異常處理器。2. 自定義ThreadPoolExecutor, 重寫afterExecute方法 ,在afterExecute方法中感知異常。 |
Thread | 使用線程池ExecutorService#submit |
自定義ThreadPoolExecutor, 重寫afterExecute方法 ,在afterExecute方法中感知異常。 |
Runnable | 不使用線程池 |
1. Thread.setDefaultUncaughtExceptionHandler 設置默認的線程異常處理器。2. Thread實例.setUncaughtExceptionHandler 給當指定線程設置線程異常處理器。 |
Runnable | 使用線程池ExecutorService#execute |
1. Thread.setDefaultUncaughtExceptionHandler 設置默認的線程異常處理器。2. 自定義ThreadPoolExecutor, 重寫afterExecute方法 ,在afterExecute方法中感知異常。 |
Runnable | 使用線程池ExecutorService#submit |
自定義ThreadPoolExecutor, 重寫afterExecute方法 ,在afterExecute方法中感知異常。 |
Callable | 不使用線程池 |
在使用Future#get()方法獲取Callable#call()的返回結果時, try-catch獲取異常 。注:此方式,若不使用Future#get()獲取結果,那麼 異步線程的異常信息將會被吞掉。 |
Callable | 使用線程池ExecutorService#submit |
自定義ThreadPoolExecutor, 重寫afterExecute方法 ,在afterExecute方法中感知異常。 |
捕獲異步線程異常的常用方式(示例說明):
提示: 完整測試項目可見文末鏈接。
準備工作:
- 一個Thread
- 一個Runnable
- 一個Callable
- 一個UncaughtExceptionHandler
- 一個ThreadPoolExecutor
Thread不使用線程池:
- 方式一:Thread.setDefaultUncaughtExceptionHandler
/** * [不使用線程池]方式一: Thread.setDefaultUncaughtExceptionHandler 設置默認的線程異常處理器 */ private static void testThread1() { Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); MyThread myThread = new MyThread(); myThread.start(); }
- 方式二:Thread實例.setUncaughtExceptionHandler
/** * [不使用線程池]方式二: Thread實例.setUncaughtExceptionHandler 給當指定線程設置線程異常處理器 */ private static void testThread2() { MyThread myThread = new MyThread(); myThread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); myThread.start(); }
Thread使用線程池ExecutorService#execute:
- 方式一:Thread.setDefaultUncaughtExceptionHandler
/** * [使用線程池execute]方式一: Thread.setDefaultUncaughtExceptionHandler 設置默認的線程異常處理器 */ private static void testThread3() { Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); ExecutorService executorService = Executors.newFixedThreadPool(3); MyThread myThread = new MyThread(); executorService.execute(myThread); }
- 方式二:自定義ThreadPoolExecutor, 重寫afterExecute方法
/** * [使用線程池execute]方式二: 自定義ThreadPoolExecutor, 重寫afterExecute方法,在afterExecute方法中感知異常 */ private static void testThread4() { ExecutorService executorService = new MyThreadPoolExecutor(5, 50, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), Thread::new, new ThreadPoolExecutor.AbortPolicy()); MyThread myThread = new MyThread(); executorService.execute(myThread); }
Thread使用線程池ExecutorService#submit:
- 方式:自定義ThreadPoolExecutor, 重寫afterExecute方法
/** * [使用線程池submit]方式一: 自定義ThreadPoolExecutor, 重寫afterExecute方法,在afterExecute方法中感知異常 */ private static void testThread5() { ExecutorService executorService = new MyThreadPoolExecutor(5, 50, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), Thread::new, new ThreadPoolExecutor.AbortPolicy()); MyThread myThread = new MyThread(); executorService.submit(myThread); }
Runnable不使用線程池:
- 方式一:Thread.setDefaultUncaughtExceptionHandler
/** * [不使用線程池]方式一: Thread.setDefaultUncaughtExceptionHandler 設置默認的線程異常處理器 */ private static void testRunnable1() { Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable).start(); }
- 方式二:Thread實例.setUncaughtExceptionHandler
/** * [不使用線程池]方式二: Thread實例.setUncaughtExceptionHandler 給當指定線程設置線程異常處理器 */ private static void testRunnable2() { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); thread.start(); }
Runnable使用線程池ExecutorService#execute:
- 方式一:Thread.setDefaultUncaughtExceptionHandler
/** * [使用線程池execute]方式一: Thread.setDefaultUncaughtExceptionHandler 設置默認的線程異常處理器 */ private static void testRunnable3() { Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); ExecutorService executorService = Executors.newFixedThreadPool(3); MyRunnable myRunnable = new MyRunnable(); executorService.execute(myRunnable); }
- 方式二:自定義ThreadPoolExecutor, 重寫afterExecute方法
/** * [使用線程池execute]方式二: 自定義ThreadPoolExecutor, 重寫afterExecute方法,在afterExecute方法中感知異常 */ private static void testRunnable4() { ExecutorService executorService = new MyThreadPoolExecutor(5, 50, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), Thread::new, new ThreadPoolExecutor.AbortPolicy()); MyRunnable myRunnable = new MyRunnable(); executorService.execute(myRunnable); }
Runnable使用線程池ExecutorService#submit:
- 方式:自定義ThreadPoolExecutor, 重寫afterExecute方法
/** * [使用線程池submit]方式一: 自定義ThreadPoolExecutor, 重寫afterExecute方法,在afterExecute方法中感知異常 */ private static void testRunnable5() { ExecutorService executorService = new MyThreadPoolExecutor(5, 50, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), Thread::new, new ThreadPoolExecutor.AbortPolicy()); MyRunnable myRunnable = new MyRunnable(); executorService.submit(myRunnable); }
Callable不使用線程池:
- 方式:在使用Future#get()方法獲取Callable#call()的返回結果時, try-catch獲取異常
/** * [不使用線程池]: 在使用Future#get()方法獲取Callable#call()的返回結果時, try-catch獲取異常 * * 注: 此方式, 若不使用Future#get()獲取結果,那麼 異步線程的異常信息將會被吞掉 */ private static void testCallable1() throws InterruptedException { MyCallable myCallable = new MyCallable(); FutureTask<Object> futureTask = new FutureTask<> (myCallable); Thread thread = new Thread(futureTask); thread.start(); try { // 在嘗試獲取結果時,捕獲異常 Object o = futureTask.get(); } catch (ExecutionException e) { Throwable cause = e.getCause(); log.error(" callable-thread occur exception", cause); } }
Callable使用線程池ExecutorService#submit:
- 方式:自定義ThreadPoolExecutor, 重寫afterExecute方法
/** * [使用線程池submit]: 自定義ThreadPoolExecutor, 重寫afterExecute方法,在afterExecute方法中感知異常 */ private static void testCallable2() { ExecutorService executorService = new MyThreadPoolExecutor(5, 50, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), Thread::new, new ThreadPoolExecutor.AbortPolicy()); MyCallable myCallable = new MyCallable(); executorService.submit(myCallable); }
個人推薦:
- 如果
異步線程不需要返回值
的話,個人推薦創建線程用Runnable
,執行線程用ExecutorService#execute
,異常處理用Thread.setDefaultUncaughtExceptionHandler
; - 如果
異步線程需要返回值
的話,個人推薦創建線程用Callable
,執行線程用ExecutorService#submit
,異常處理用主動編碼對Future#get進行try-catch的方式
。
^_^ 如有不當之處,歡迎指正
^_^ 測試代碼託管鏈接
https://github.com/JustryDeng…CatchSyncThreadException…
^_^ 本文已經被收錄進《程序員成長筆記(三)》,筆者JustryDeng