前言
繼之前的文章詳解jvm如何處理異常,今天再次發(fā)布一篇比較關(guān)聯(lián)的文章,如題目可知,今天聊一聊在jvm中線程遇到未捕獲異常的問(wèn)題,其中涉及到線程如何處理未捕獲異常和一些內(nèi)容介紹。
什么是未捕獲異常
未捕獲異常指的是我們?cè)诜椒w中沒(méi)有使用try-catch捕獲的異常,比如下面的例子
1
2
3
4
5
6
7
|
private static void testuncaughtexception(string arg) { try { system.out.println( 1 / arg.length()); } catch (arithmeticexception e) { e.printstacktrace(); } } |
上面的代碼很有可能發(fā)生如下情況
- 如果方法參數(shù)arg傳遞null,會(huì)出現(xiàn)nullpointerexception
- 如果參數(shù)arg傳遞內(nèi)容為空的字符串(“”),會(huì)出現(xiàn)arithmeticexception
對(duì)于上面的問(wèn)題,我們不難發(fā)現(xiàn)
- 上面可能出現(xiàn)的nullpointerexception和arithmeticexception都屬于unchecked exceptions
- 而arithmeticexception被我們?nèi)藶閠ry-catch捕獲了,它不符合本文對(duì)于未捕獲異常的定義
- nullpointerexception 由于我們沒(méi)有catch住,就變成了我們要聊的未捕獲異常
- 另外,未捕獲異常實(shí)際是unchecked exceptions的子集
uncaughtexceptionhandler 是什么
- 它是線程遇到未捕獲異常的一個(gè)處理者接口
-
它包含一個(gè)方法
void uncaughtexception(thread t, throwable e);
用來(lái)處理接收處理異常發(fā)生后的操作,比如收集崩潰信息并上報(bào)等 -
可以通過(guò) 實(shí)例方法
thread.setuncaughtexceptionhandler
為某一個(gè)thread實(shí)例設(shè)置未捕獲異常處理者 -
也可以通過(guò) 靜態(tài)方法
thread.setdefaultuncaughtexceptionhandler
設(shè)置所有thread實(shí)例的未捕獲異常處理者
threadgroup 是什么
- threadgroup 是線程的集合
- threadgroup 也可以包含子threadgroup
- 除了初始的threadgroup 之外,每個(gè)threadgroup都有一個(gè)父 threadgroup
- threadgroup 自身實(shí)現(xiàn)了thread.uncaughtexceptionhandler,用來(lái)相應(yīng)處理其內(nèi)部的線程和threadgroup發(fā)生未捕獲異常。
未捕獲異常處理者 設(shè)置指南
線程發(fā)生了未捕獲異常,jvm怎么處理
分發(fā)throwable實(shí)例
當(dāng)線程a中出現(xiàn)了未捕獲異常時(shí),jvm會(huì)調(diào)用線程a的dispatchuncaughtexception(throwable)
方法
1
2
3
4
5
6
7
|
/** * 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); } |
獲取未捕獲異常處理者
每個(gè)線程會(huì)有一個(gè)變量(uncaughtexceptionhandler)來(lái)保存未捕獲異常的處理者
在線程需要確定throwable分發(fā)目標(biāo)的處理者時(shí),優(yōu)先獲取當(dāng)前線程中uncaughtexceptionhandler變量
如果出問(wèn)題線程的uncaughtexceptionhandler為null(即沒(méi)有顯式設(shè)置異常處理者),則使用自己所在的threadgroup來(lái)作為未捕獲異常處理者。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/** * returns the handler invoked when this thread abruptly terminates * due to an uncaught exception. if this thread has not had an * uncaught exception handler explicitly set then this thread's * <tt>threadgroup</tt> object is returned, unless this thread * has terminated, in which case <tt>null</tt> is returned. * @since 1.5 * @return the uncaught exception handler for this thread */ public uncaughtexceptionhandler getuncaughtexceptionhandler() { return uncaughtexceptionhandler != null ? uncaughtexceptionhandler : group; } |
如果throwable分發(fā)給threadgroup
- threadgroup會(huì)嘗試轉(zhuǎn)給它的父threadgroup(如果存在的話(huà))
- 如果上面沒(méi)有找到對(duì)應(yīng)的threadgroup,則嘗試獲取thread.getdefaultuncaughtexceptionhandler()并分發(fā)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
/** * called by the java virtual machine when a thread in this * thread group stops because of an uncaught exception, and the thread * does not have a specific {@link thread.uncaughtexceptionhandler} * installed. * <p> * the <code>uncaughtexception</code> method of * <code>threadgroup</code> does the following: * <ul> * <li>if this thread group has a parent thread group, the * <code>uncaughtexception</code> method of that parent is called * with the same two arguments. * <li>otherwise, this method checks to see if there is a * {@linkplain thread#getdefaultuncaughtexceptionhandler default * uncaught exception handler} installed, and if so, its * <code>uncaughtexception</code> method is called with the same * two arguments. * <li>otherwise, this method determines if the <code>throwable</code> * argument is an instance of {@link threaddeath}. if so, nothing * special is done. otherwise, a message containing the * thread's name, as returned from the thread's {@link * thread#getname getname} method, and a stack backtrace, * using the <code>throwable</code>'s {@link * throwable#printstacktrace printstacktrace} method, is * printed to the {@linkplain system#err standard error stream}. * </ul> * <p> * applications can override this method in subclasses of * <code>threadgroup</code> to provide alternative handling of * uncaught exceptions. * * @param t the thread that is about to exit. * @param e the uncaught exception. * @since jdk1.0 */ 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); } } } |
將上面的處理流程做成圖的形式,就是下圖所示
注:上述圖片來(lái)自https://www.javamex.com/tutorials/exceptions/exceptions_uncaught_handler.shtml
questions
初始的threadgroup是什么
上面提到了初始的threadgroup沒(méi)有父threadgroup,是主線程所在的threadgroup么?
這個(gè)問(wèn)題,我們可以通過(guò)這樣一段代碼驗(yàn)證
1
2
3
4
5
6
7
|
private static void dumpthreadgroups() { threadgroup threadgroup = thread.currentthread().getthreadgroup(); while (threadgroup != null ) { system.out.println( "dumpthreadgroups threadgroup=" + threadgroup.getname()); threadgroup = threadgroup.getparent(); } } |
執(zhí)行該方法對(duì)應(yīng)的輸出是
dumpthreadgroups threadgroup=main
dumpthreadgroups threadgroup=system
因此我們可以發(fā)現(xiàn),初始的threadgroup是一個(gè)叫做system的threadgroup,而不是main threadgroup
setdefaultuncaughtexceptionhandler 設(shè)置的一定會(huì)被調(diào)用到么
這其實(shí)是一個(gè)很好的問(wèn)題,答案是不一定會(huì)被調(diào)用,因?yàn)榭赡艽嬖谝韵碌那闆r
- 出問(wèn)題的線程設(shè)置了對(duì)應(yīng)的uncaughtexcpetionhandler,優(yōu)先響應(yīng)分發(fā)到這個(gè)handler
- 出問(wèn)題的線程所在的threadgroup包括其祖先threadgroup 重寫(xiě)了uncaughtexception 也可能造成線程默認(rèn)的handler無(wú)法被調(diào)用
- 出問(wèn)題的線程重寫(xiě)了dispatchuncaughtexception 可能性較小
- 出問(wèn)題的線程重寫(xiě)了getuncaughtexceptionhandler 可能性較小
參考聲明
how uncaught exceptions are handled
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。
原文鏈接:https://droidyue.com/blog/2019/01/06/how-java-handle-uncaught-exceptions/