處理子線程異常(重要)、
參考:https://www.cnblogs.com/jpfss/p/10272066.html
一、Java子線程中的異常處理
父線程中啓動子線程,直接在父線程啓動子線程的地方try...catch,是捕獲不到子線程的異常的
緣由:Runnable接口的run方法的完整簽名,由於沒有標識throws語句,因此方法是不會拋出checked異常的。至於RuntimeException這樣的 unchecked異常,因爲新線程由JVM進行調度執行,若是發生了異常,也不會通知到父線程。
二、處理子線程的異常
子線程中處理:
a.子線程中try...catch
b.爲子線程設置「未捕獲異常處理器」UncaughtExceptionHandler
既然a方法已經能夠捕獲異常,爲何還要有b存在,個人理解是a須要指定可能發生異常的代碼,而b不須要指定,只要發生異常,對應的異常處理器自動處理。
父線程中處理:
c.經過Future的get方法捕獲異常(推薦)html
三、示例代碼java
1 import java.util.ArrayList; 2 import java.util.List; 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.ExecutionException; 5 import java.util.concurrent.ExecutorService; 6 import java.util.concurrent.Executors; 7 import java.util.concurrent.Future; 8 9 public class TestExceptionThred { 10 11 /** 12 * @param args 13 */ 14 public static void main(String[] args) { 15 ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5); 16 List<Future<String>> fetrueResult = new ArrayList<>(); 17 System.out.println("start"); 18 19 try { 20 System.out.println("ssssssss"); 21 // newFixedThreadPool.execute(new ChildThread01()); 22 // newFixedThreadPool.execute(new ChildThread01()); 23 // newFixedThreadPool.execute(new ChildThread01()); 24 25 // newFixedThreadPool.execute(new ChildThread0101()); 26 // newFixedThreadPool.execute(new ChildThread0101()); 27 28 // newFixedThreadPool.execute(new ChildThread02()); 29 // newFixedThreadPool.execute(new ChildThread02()); 30 31 // newFixedThreadPool.execute(new ChildThread0202()); 32 // newFixedThreadPool.execute(new ChildThread0202()); 33 34 Future<String> result01 = newFixedThreadPool.submit(new ChildThread03()); 35 fetrueResult.add(result01); 36 Future<String> result02 = newFixedThreadPool.submit(new ChildThread03()); 37 fetrueResult.add(result02); 38 for (Future<String> result : fetrueResult) { 39 result.get(); 40 } 41 System.out.println("eeeeeeeee"); 42 } catch (InterruptedException | ExecutionException e) { 43 System.out.println("InterruptedException or ExecutionException has been handled"); 44 } catch (Exception e) { 45 System.out.println("exception has been handled"); 46 } finally { 47 System.out.println("finally"); 48 if (null != newFixedThreadPool) { 49 newFixedThreadPool.shutdown(); 50 } 51 } 52 System.out.println("end"); 53 } 54 55 } 56 57 /** 58 * 子線程中發生異常,未處理直接拋出,這種狀況下,子線程直接退出,且不會記錄任何日誌 59 */ 60 class ChildThread01 implements Runnable { 61 62 /* 63 * @see java.lang.Runnable#run() 64 */ 65 @Override 66 public void run() { 67 System.out.println("ChildThread before exception"); 68 exceptionMethod(); 69 System.out.println("ChildThread before exception"); 70 } 71 72 private void exceptionMethod() { 73 throw new RuntimeException("ChildThread01 exception"); 74 } 75 } 76 77 /** 78 * 解決方案1:在子線程中try...catch捕獲異常 79 * 子線程中發生異常,並在子線程中處理 80 */ 81 /** 82 * 爲線程設置異常處理器。具體作法能夠是如下幾種: 83 * (1)Thread.setUncaughtExceptionHandler設置當前線程的異常處理器; 84 * (2)Thread.setDefaultUncaughtExceptionHandler爲整個程序設置默認的異常處理器; 85 * 若是當前線程有異常處理器(默認沒有),則優先使用該UncaughtExceptionHandler類; 86 * 不然,若是當前線程所屬的線程組有異常處理器,則使用線程組的UncaughtExceptionHandler; 87 * 不然,使用全局默認的DefaultUncaughtExceptionHandler;若是都沒有的話,子線程就會退出。 88 * 注意:子線程中發生了異常,若是沒有任何類來接手處理的話,是會直接退出的,而不會記錄任何日誌。 89 * 因此,若是什麼都不作的話,是會出現子線程任務既沒執行成功,也沒有任何日誌提示的「詭異」現象的。 90 */ 91 class ChildThread0101 implements Runnable { 92 93 /* 94 * @see java.lang.Runnable#run() 95 */ 96 @Override 97 public void run() { 98 //可能發生異常的地方,用try...catch處理 99 try { 100 System.out.println("ChildThread0101 before exception"); 101 exceptionMethod(); 102 System.out.println("ChildThread0101 before exception"); 103 } catch (Exception e) { 104 System.out.println("exception has been handled in ChildThread0101"); 105 } 106 } 107 108 private void exceptionMethod() { 109 throw new RuntimeException("ChildThread0101 exception"); 110 } 111 } 112 113 /** 114 * 解決方案2:爲子線程設置「未捕獲異常處理器」UncaughtExceptionHandler 115 * 子線程中發生異常,並在子線程中處理 116 */ 117 class ChildThread02 implements Runnable { 118 private static ChildThreadExceptionHandler exceptionHandler; 119 120 static { 121 exceptionHandler = new ChildThreadExceptionHandler(); 122 } 123 124 @Override 125 public void run() { 126 //下面代碼可能會發生異常,可是不須要用try...catch顯示的包裹代碼處理, 127 //定義的異常處理器會自動捕獲異常,並在子線程中處理 128 129 //設置當前線程的異常處理器 130 Thread.currentThread().setUncaughtExceptionHandler(exceptionHandler); 131 System.out.println("ChildThread02 do something 1"); 132 exceptionMethod(); 133 System.out.println("ChildThread02 do something 2"); 134 } 135 136 private void exceptionMethod() { 137 throw new RuntimeException("ChildThread02 exception"); 138 } 139 140 //爲線程設置「未捕獲異常處理器」UncaughtExceptionHandler 141 public static class ChildThreadExceptionHandler implements Thread.UncaughtExceptionHandler { 142 public void uncaughtException(Thread t, Throwable e) { 143 System.out.println(String.format("handle exception in ChildThread02. %s", e)); 144 } 145 } 146 } 147 148 /** 149 * 解決方案2 150 * 子線程中發生異常,並在子線程中處理 151 */ 152 class ChildThread0202 implements Runnable { 153 private static ChildThreadExceptionHandler exceptionHandler; 154 155 static { 156 exceptionHandler = new ChildThreadExceptionHandler(); 157 //設置全部線程的默認異常處理器 158 Thread.setDefaultUncaughtExceptionHandler(exceptionHandler); 159 } 160 161 public void run() { 162 System.out.println("ChildThread0202 do something 1"); 163 exceptionMethod(); 164 System.out.println("ChildThread0202 do something 2"); 165 } 166 167 private void exceptionMethod() { 168 throw new RuntimeException("ChildThread0202 exception"); 169 } 170 171 public static class ChildThreadExceptionHandler implements Thread.UncaughtExceptionHandler { 172 public void uncaughtException(Thread t, Throwable e) { 173 System.out.println(String.format("handle exception in ChildThread0202. %s", e)); 174 } 175 } 176 } 177 178 /** 179 * 解決方案3:經過Future的get方法捕獲異常(推薦) 180 */ 181 /** 182 * 使用線程池提交一個能獲取到返回信息的方法,也就是ExecutorService.submit(Callable) 183 * 在submit以後能夠得到一個線程執行結果的Future對象,而若是子線程中發生了異常, 184 * 經過future.get()獲取返回值時,能夠捕獲到ExecutionException異常,從而知道子線程中發生了異常 185 * 186 * 注意,若是不調用future.get(),則不會捕獲到異常;若是子線程發生異常,直接退出,無任何記錄 187 * 若是啓動了多個子線程,捕獲到任何一個子線程的異常,父線程執行finally語句或執行後續代碼 188 */ 189 class ChildThread03 implements Callable<String> { 190 public String call() throws Exception { 191 System.out.println("ChildThread03 do something 1"); 192 exceptionMethod(); 193 System.out.println("ChildThread03 do something 2"); 194 return "ChildThread03 test result"; 195 } 196 197 private void exceptionMethod() { 198 throw new RuntimeException("ChildThread03 exception"); 199 } 200 }