FixedThreadPool吞掉了异常
2019獨角獸企業重金招聘Python工程師標準>>>
FixedThreadPool吞掉了異常
為了方便遍描述問題,如下是簡化后的
public class RunException {public static void main(String[] args) {ExecutorService readerPool = Executors.newFixedThreadPool(3);readerPool.submit(new Runnable() {public void run() {throw new RuntimeException("異常");}});readerPool.shutdown();} }此處FixedThreadPool吞掉了異常。
問題
為什么不能拋出到外部線程捕獲
jvm會在線程即將死掉的時候捕獲所有未捕獲的異常進行處理。默認使用的是Thread.defaultUncaughtExceptionHandler
submit為什么不能打印報錯信息
public Future<?> submit(Runnable task) {if (task == null) throw new NullPointerException();RunnableFuture<Void> ftask = newTaskFor(task, null);//創建FutureTask類execute(ftask);return ftask;}查看FutureTask.run():
public void run() {if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return;try {Callable<V> c = callable;if (c != null && state == NEW) {V result;boolean ran;try {result = c.call();ran = true;} catch (Throwable ex) {result = null;ran = false;//這里捕獲了所有異常調用setExceptionsetException(ex);}if (ran)set(result);}} finally {// runner must be non-null until state is settled to// prevent concurrent calls to run()runner = null;// state must be re-read after nulling runner to prevent// leaked interruptsint s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}}接著查看setException(ex);,將線程狀態由completing改為exceptional,并將異常信息存在outcome中:
//這個方法就是這事線程狀態為completing -> exceptional//同時用outcome保存異常信息。protected void setException(Throwable t) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {outcome = t;UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final statefinishCompletion();}}繼續查看outcome的使用:
//report會拋出exception信息 private V report(int s) throws ExecutionException {Object x = outcome;if (s == NORMAL)return (V)x;if (s >= CANCELLED)throw new CancellationException();throw new ExecutionException((Throwable)x);}//get會調用report()方法 public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING)s = awaitDone(false, 0L);return report(s);}所以如果需要獲取異常信息就需要調用get()方法。
execute怎么輸入logger日志
查看execute的實現ThreadPoolExecutor.execute():
public void execute(Runnable command) {if (command == null)throw new NullPointerException();/** Proceed in 3 steps:** 1. If fewer than corePoolSize threads are running, try to* start a new thread with the given command as its first* task. The call to addWorker atomically checks runState and* workerCount, and so prevents false alarms that would add* threads when it shouldn't, by returning false.** 2. If a task can be successfully queued, then we still need* to double-check whether we should have added a thread* (because existing ones died since last checking) or that* the pool shut down since entry into this method. So we* recheck state and if necessary roll back the enqueuing if* stopped, or start a new thread if there are none.** 3. If we cannot queue task, then we try to add a new* thread. If it fails, we know we are shut down or saturated* and so reject the task.*/int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))reject(command);}從代碼可知,線程池將任務加入了任務隊列,需要看看線程在哪執行任務的。那么只需要看看有沒有獲取任務的函數,ThreadPoolExecutor.getTask()即是獲取任務的函數,通過查找,ThreadPoolExecutor.runWorker調用了ThreadPoolExecutor.getTask(),它應該是執行任務的代碼:
final void runWorker(Worker w) {Thread wt = Thread.currentThread();Runnable task = w.firstTask;w.firstTask = null;w.unlock(); // allow interruptsboolean completedAbruptly = true;try {while (task != null || (task = getTask()) != null) {w.lock();// If pool is stopping, ensure thread is interrupted;// if not, ensure thread is not interrupted. This// requires a recheck in second case to deal with// shutdownNow race while clearing interruptif ((runStateAtLeast(ctl.get(), STOP) ||(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP))) &&!wt.isInterrupted())wt.interrupt();try {beforeExecute(wt, task);Throwable thrown = null;try {task.run();} catch (RuntimeException x) {//這里直接拋出所有Runtime異常thrown = x; throw x;} catch (Error x) {thrown = x; throw x;} catch (Throwable x) {thrown = x; throw new Error(x);} finally {afterExecute(task, thrown);}} finally {task = null;w.completedTasks++;w.unlock();}}completedAbruptly = false;} finally {processWorkerExit(w, completedAbruptly);}}代碼注釋中看到獲取RuntimeException的位置了。
這里拋出的異常在哪里處理呢? 接下來處理是交由jvm處理,從已經學習的知識中只知道jvm調用Thread.dispatchUncaughtException來處理所有未捕獲的異常
/*** Dispatch an uncaught exception to the handler. This method is* intended to be called only by the JVM.*/private void dispatchUncaughtException(Throwable e) {getUncaughtExceptionHandler().uncaughtException(this, e);}這里可以根據該方法注釋解釋,意思就是這個方法只用于JVM調用,處理線程未捕獲的異常。 繼續查看getUncaughtExceptionHandler()方法:
public interface UncaughtExceptionHandler {svoid uncaughtException(Thread t, Throwable e);}// 處理類private volatile UncaughtExceptionHandler uncaughtExceptionHandler;// 默認處理類private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;/*** 設置默認的處理類,注意是靜態方法,作用域為所有線程設置默認的處理類**/public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {SecurityManager sm = System.getSecurityManager();if (sm != null) {sm.checkPermission(new RuntimePermission("setDefaultUncaughtExceptionHandler"));}defaultUncaughtExceptionHandler = eh;}//獲取默認處理類public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){return defaultUncaughtExceptionHandler;}//獲取處理類,注意不是靜態方法,只作用域該線程//處理類為空使用ThreadGrouppublic UncaughtExceptionHandler getUncaughtExceptionHandler() {return uncaughtExceptionHandler != null ?uncaughtExceptionHandler : group;}//設置處理類public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {checkAccess();uncaughtExceptionHandler = eh;}/*** Dispatch an uncaught exception to the handler. This method is* intended to be called only by the JVM.*/private void dispatchUncaughtException(Throwable e) {//獲取處理類型進行異常處理getUncaughtExceptionHandler().uncaughtException(this, e);}如果線程UncaughtExceptionHandler處理器為空則threadGroup處理器 查看threadGroup:
public void uncaughtException(Thread t, Throwable e) {if (parent != null) {parent.uncaughtException(t, e);} else {Thread.UncaughtExceptionHandler ueh =Thread.getDefaultUncaughtExceptionHandler();if (ueh != null) {ueh.uncaughtException(t, e);} else if (!(e instanceof ThreadDeath)) {System.err.print("Exception in thread \""+ t.getName() + "\" ");e.printStackTrace(System.err);}}}從代碼中可以看出,
所以有兩個方法實現用logger輸出:
測試程序
僅某個線程設置默認UncaughtExceptionHandler
public static void oneThreadUncaughtExceptionHandler() {Thread t1 = new Thread(() -> {throw new RuntimeException(" t1 runtime exception");}, "t1");t1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(Thread.currentThread() + "trigger uncaugh exception handler");}});t1.start();Thread t2 = new Thread(() -> {throw new RuntimeException(" t2 runtime exception");}, "t2");t2.start();}設置defaultUncaughtExceptionHandler
public static void defaultThreadUncaughtExceptionHandler() {Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(Thread.currentThread() + "trigger uncaugh exception handler");}});new Thread(() -> {throw new RuntimeException(" t1 runtime exception");}, "t1").start();new Thread(() -> {throw new RuntimeException(" t2 runtime exception");}, "t2").start();}解惑
那為什么我們的例子代碼中,異常不會輸出呢?應該有兜底的System.err來輸出異常才對。 不是這樣的,我們的例子中的異常實際上是處理了的,它捕獲了異常,并且保存到了outcome中。僅僅有未捕獲的異常,JVM才會調用Thread.dispatchUncaughtException來處理。
轉載于:https://my.oschina.net/hgfdoing/blog/3043237
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的FixedThreadPool吞掉了异常的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Google Chrome等浏览器不允许
- 下一篇: zabbix—自动发现端口并监控