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吞掉了異常。

問題

  1. 為什么不能拋出到外部線程捕獲
  2. submit為什么不能打印報錯信息
  3. execute怎么使用logger打印報錯信息

為什么不能拋出到外部線程捕獲

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; //這里捕獲了所有異常調用setException setException(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 interrupts int 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 state finishCompletion(); } } 

繼續查看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); } 
  1. report會拋出exception信息,但report是私有方法;
  2. get會調用report()方法

所以如果需要獲取異常信息就需要調用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 interrupts boolean 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 interrupt if ((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 {s void 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; } //獲取處理類,注意不是靜態方法,只作用域該線程 //處理類為空使用ThreadGroup public 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(www.mumingyue.cn).uncaughtException(this, e); } 

如果線程UncaughtExceptionHandler處理器為空則threadGroup處理器 查看threadGroup:

    public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t,www.douniu2.cc 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); } } } 

從代碼中可以看出,

  1. 如果父進程不為空,則使用父進程處理未捕獲異常;
  2. 如果無父進程,則獲取默認的UncaughtExceptionHandler進行處理。
    1. 默認的UncaughtExceptionHandler為null,則使用Sytem.err將錯誤信息輸出;
    2. 默認的UncaughtExceptionHandler不為null,則使用UncaughtExceptionHandler進行處理。

所以有兩個方法實現用logger輸出:

  1. Thread定義uncaughtExceptionHandlerThread.setUncaughtExceptionHandler(www.tianscpt.com),該方法僅能設置某個線程的默認UncaughtExceptionHandler
  2. Thread定義defaultUncaughtExceptionHandler:使用Thread.setDefaultUncaughtExceptionHandler,該方法設置所有線程的默認UncaughtExceptionHandler

測試程序

僅某個線程設置默認UncaughtExceptionHandler

public static void oneThreadUncaughtExceptionHandler() { Thread t1 = new Thread((www.mhylpt.com/) -> { throw new RuntimeException(" t1 runtime exception"); }, "t1"); t1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println(Thread.currentThread(www.baihuiyulep.cn) + "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() { @Override public void uncaughtException(www.ysyl157.com Thread t, Throwable e) { System.out.println(Thread.currentThread() + "trigger uncaugh exception handler"); } }); new Thread(() -> { throw new RuntimeException(www.tianjiuyule178.com??" t1 runtime exception"); }, "t1").start(); new Thread(() -> { throw new RuntimeException(" t2 runtime exception"); }, "t2").start(); } 

解惑

那為什么我們的例子代碼中,異常不會輸出呢?應該有兜底的System.err來輸出異常才對。 不是這樣的,我們的例子中的異常實際上是處理了的,它捕獲了異常,并且保存到了outcome中。僅僅有未捕獲的異常,JVM才會調用Thread.dispatchUncaughtException來處理。

轉載于:https://www.cnblogs.com/qwangxiao/p/10780403.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/277842.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/277842.shtml
英文地址,請注明出處:http://en.pswp.cn/news/277842.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Teams Meeting App的 task 彈出框

前幾篇文章我們介紹了 Teams Meeting App 的各種類型和如何從無到有的使用 net6 和 c# 來開發一個 Teams Meeting app&#xff0c;那今天我們開始討論一些 meeting app 的高級互動&#xff1a; task 彈出框。我們先來快速修改一下之前的代碼&#xff0c;看看什么是 task 彈出框…

react 學習

react官網地址&#xff1a;http://facebook.github.io/react/ webpack官網地址&#xff1a;http://webpack.js.org/ 英文 https://www.webpackjs.com/ 中文 參考資料&#xff1a; React 入門實例教程-阮一峰 webpack的學習 學習列表&#xff1a; 了解react的語法&#x…

如何獲取Teams Meeting 詳情

最近有一些朋友問我&#xff0c;有沒有可能獲取到會議的詳情&#xff0c;我搜索了目前所有的 teams 文檔&#xff0c;發現有一個api可以獲取&#xff0c;不過在我寫這篇文章的時候&#xff0c;這個 api 還在 preview 階段&#xff0c;可能在正式發布前&#xff0c;還會有一些變…

C++ : 內聯函數和引用變量

一.內聯函數 內聯函數和普通函數的使用方法沒有本質區別&#xff0c;我們來看一個例子&#xff0c;下面展示了內聯函數的使用方法&#xff1a; #include <iostream> using namespace std; //下面展示內聯函數的使用 inline double square(double x) {return (x*x);} int…

Teams Meeting 實時事件通知

Microsoft Teams最近推出了很多新的功能和api&#xff0c;我們今天就來一起看一下 teams 會議的實時事件通知&#xff0c;我覺得有了這個功能&#xff0c;我們的app&#xff0c;我們的bot又可以有很多可以實現的場景了。 我們來看看如何在 c# 里處理會議開始和結束這兩個事件。…

error記錄 | 不能將參數 1 從“const char [5]”轉換為“LPCTSTR

Windows使用兩種字符集ANSI和UNICODE&#xff0c;前者就是通常使用的單字節方式&#xff0c;但這種方式處理象中文這樣的雙字節字符不方便&#xff0c;容易出現半個漢字的情況。而后者是雙字節方式&#xff0c;方便處理雙字節字符。Windows NT的所有與字符有關的函數都提供兩…

JMM 學習筆記

并發編程的模型 并發編程需要解決的兩個問題&#xff1a;線程之間如何同步&#xff0c;線程之間如何通信。 線程之間通信&#xff1a;共享內存&#xff0c;消息傳遞。 共享內存通過線程之間讀-寫程序的公共狀態進行通信。消息傳遞要通過線程之間主動傳遞消息進行通信。 線程之間…

嵌套函數,匿名函數,高階函數

目錄 嵌套函數匿名函數高階函數嵌套函數 就是在函數里再定義一個函數 # 1,函數內部可以在定義函數 # 2,函數要想執行&#xff0c;必須要先被調用 def name1():print(kk)def name2():print(vfx)name2() name1() 輸出&#xff1a; kk vfx name2 現在他內部代碼找輸出&#xff0c;…

Teams Developer Portal介紹

在去年的 Build2021 大會上講到的 Teams Developer Portal 已經上線一段時間了&#xff0c;我這幾天玩了一下&#xff0c;發現比之前的 app studio 強大了很多&#xff0c;所以趕快寫篇文章和大家分享。 Developer Portal 有兩種訪問的方式&#xff0c;一個是網頁版&#xff0…

使用環境變量來配置 Teams App 的 manifest

上篇文章我們介紹了 Teams 的 Developer Portal&#xff0c;今天我想分享一個dev portal里一個比較實用的功能。這個功能在之前的 App Studio 里沒有。這個功能叫 Environment variables。 當我們真實開發一個 teams app的時候&#xff0c;肯定有自己的開發環境&#xff0c;測…

[Unity優化]批處理03:靜態批處理

[Unity優化]批處理03&#xff1a;靜態批處理 原理&#xff1a; 運行時&#xff0c;把需要進行靜態批處理的網格合并到一個新的網格中。雖然只進行一次合并操作&#xff0c;但是會占用更多的內存來存儲合并后的網格&#xff0c;并且被靜態批處理的物體無法移動旋轉縮放 要使用靜…

制造領域的人工智能技術

“AI將執行制造、質量控制、縮短設計時間、減少材料浪費、提高生產再利用率&#xff0c;執行預測性維護等等&#xff0c;盡管人工智能有望從根本上改變很多行業&#xff0c;但該技術非常適合制造業”Ng說。Andrew Ng是深度學習Google Brain項目的創始人兼斯坦福大學計算機科學兼…

如何獲取一個會議的 transcripts

Teams 開發團隊在過去半年里提供了很多的關于會議的 api&#xff0c;這讓我們又有了很多的可以實現的功能和場景。今天我要介紹的是如何獲取會議的 transcripts。 首先我們要知道的一個概念是&#xff1a;一個會議 meeting 可能有很多的 transcript&#xff0c;是一對多的關系…

JS獲取IP地址

HTML代碼&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><script src"https://unpkg.com/vue/dist/vue.js"></script></head><body><div id"vm&quo…

1小時玩爆趣頭條自媒體平臺,增粉實戰操作分享

做自媒體的人最關注的就是每天自己賬號的后臺數據&#xff0c;因為數據決定當天的收益。因此只要每天能達到幾十萬的數據&#xff0c;相信對于做自媒體的朋友來說&#xff0c;一個月下來&#xff0c;最少也有1萬以上的收入。目前&#xff0c;自媒體平臺能賺錢的平臺有很多&…

營業額統計

傳送門 這個題...裸題啊,裸的不能再裸了 按天數插入,每次插入之后,比較和前驅后繼的差,取 min 統計入答案即可 注意之前已經插入過的值就不需要插入了.然后這題就 A 了 Code: #include <iostream> #include <cstdlib> #include <cstdio> #include <ctime&…

React setStats數組不更新,百思不得其解。

樓樓今日遇到個坑爹的問題。 就是 this.setStats({}) 對 this.stats 不更新問題 問題是這樣的 constructor(props) {super(props);this.state {imageList: []}WechatService.getMaterialOrealList("image").then((result) > {this.setState({imageList: result})…

隧道6in4 和隧道6to4(GNS3)

隧道6in4實驗配置 拓撲圖 Device Interface IP Address&#xff08;IPv6&#xff09; R1 F 0/0 10.1.81.1 F 0/1 2001:db8:cafe:81::10 R2 F 0/0 10.81.1.2 F 0/1 172.81.1.2 R3 F 0/0 172.81.1.3 F 0/1 2001:DB8:ACE:81::20 R4 F 0/0 2001:db8:cafe:81::4…

hadoop常用命令總結

2019獨角獸企業重金招聘Python工程師標準>>> 一、前述 分享一篇hadoop的常用命令的總結&#xff0c;將常用的Hadoop命令總結如下。 二、具體 1、啟動hadoop所有進程 start-all.sh等價于start-dfs.sh start-yarn.sh 但是一般不推薦使用start-all.sh(因為開源框架中內…

C面向對象編程

C語言面向對象編程 1. 定義一個SuperObject結構體, 里面包含最少的元素, 但是確實每一個對象都含有的, 這樣可以實現多態2. 每一個對象都是基于類的, 我們知道類都是單例對象, 所以我們創建結構體, TypeObject(類似于Java中的class), 接著每一個Object結構體中 都包含著一個對應…