处理子线程异常(重要)、
参考: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 }