FixedThreadPool吞掉了异常
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
FixedThreadPool吞掉了異常
為了方便遍描述問(wèn)題,如下是簡(jiǎn)化后的
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吞掉了異常。
問(wèn)題
為什么不能拋出到外部線程捕獲
jvm會(huì)在線程即將死掉的時(shí)候捕獲所有未捕獲的異常進(jìn)行處理。默認(rèn)使用的是Thread.defaultUncaughtExceptionHandler
submit為什么不能打印報(bào)錯(cuò)信息
public Future<?> submit(Runnable task) {if (task == null) throw new NullPointerException();RunnableFuture<Void> ftask = newTaskFor(task, null);//創(chuàng)建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;//這里捕獲了所有異常調(diào)用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);,將線程狀態(tài)由completing改為exceptional,并將異常信息存在outcome中:
//這個(gè)方法就是這事線程狀態(tài)為completing -> exceptional//同時(shí)用outcome保存異常信息。protected void setException(Throwable t) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {outcome = t;UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final statefinishCompletion();}}繼續(xù)查看outcome的使用:
//report會(huì)拋出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會(huì)調(diào)用report()方法 public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING)s = awaitDone(false, 0L);return report(s);}所以如果需要獲取異常信息就需要調(diào)用get()方法。
execute怎么輸入logger日志
查看execute的實(shí)現(xiàn)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);}從代碼可知,線程池將任務(wù)加入了任務(wù)隊(duì)列,需要看看線程在哪執(zhí)行任務(wù)的。那么只需要看看有沒(méi)有獲取任務(wù)的函數(shù),ThreadPoolExecutor.getTask()即是獲取任務(wù)的函數(shù),通過(guò)查找,ThreadPoolExecutor.runWorker調(diào)用了ThreadPoolExecutor.getTask(),它應(yīng)該是執(zhí)行任務(wù)的代碼:
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的位置了。
這里拋出的異常在哪里處理呢? 接下來(lái)處理是交由jvm處理,從已經(jīng)學(xué)習(xí)的知識(shí)中只知道jvm調(diào)用Thread.dispatchUncaughtException來(lái)處理所有未捕獲的異常
/*** 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);}這里可以根據(jù)該方法注釋解釋,意思就是這個(gè)方法只用于JVM調(diào)用,處理線程未捕獲的異常。 繼續(xù)查看getUncaughtExceptionHandler()方法:
public interface UncaughtExceptionHandler {svoid uncaughtException(Thread t, Throwable e);}// 處理類private volatile UncaughtExceptionHandler uncaughtExceptionHandler;// 默認(rèn)處理類private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;/*** 設(shè)置默認(rèn)的處理類,注意是靜態(tài)方法,作用域?yàn)樗芯€程設(shè)置默認(rèn)的處理類**/public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {SecurityManager sm = System.getSecurityManager();if (sm != null) {sm.checkPermission(new RuntimePermission("setDefaultUncaughtExceptionHandler"));}defaultUncaughtExceptionHandler = eh;}//獲取默認(rèn)處理類public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){return defaultUncaughtExceptionHandler;}//獲取處理類,注意不是靜態(tài)方法,只作用域該線程//處理類為空使用ThreadGrouppublic UncaughtExceptionHandler getUncaughtExceptionHandler() {return uncaughtExceptionHandler != null ?uncaughtExceptionHandler : group;}//設(shè)置處理類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) {//獲取處理類型進(jìn)行異常處理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);}}}從代碼中可以看出,
所以有兩個(gè)方法實(shí)現(xiàn)用logger輸出:
測(cè)試程序
僅某個(gè)線程設(shè)置默認(rèn)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();}設(shè)置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();}解惑
那為什么我們的例子代碼中,異常不會(huì)輸出呢?應(yīng)該有兜底的System.err來(lái)輸出異常才對(duì)。 不是這樣的,我們的例子中的異常實(shí)際上是處理了的,它捕獲了異常,并且保存到了outcome中。僅僅有未捕獲的異常,JVM才會(huì)調(diào)用Thread.dispatchUncaughtException來(lái)處理。
轉(zhuǎn)載于:https://my.oschina.net/hgfdoing/blog/3043237
超強(qiáng)干貨來(lái)襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的FixedThreadPool吞掉了异常的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Google Chrome等浏览器不允许
- 下一篇: zabbix—自动发现端口并监控