当前线程内的异常
结论:在当前线程通过try catch
可以捕获当前线程抛出的异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package pers.fulsun;import java.util.logging.ConsoleHandler;import java.util.logging.Level;import java.util.logging.LogManager;import java.util.logging.Logger;public class ExceptionInCurThread { public static void main (String[] args) { try { throw new RuntimeException ("在主线程抛出异常,在主线程捕获" ); } catch (Exception e) { log.info("捕获到异常" + e); e.printStackTrace(); } } }
其他线程抛出的异常 无效:使用try-catch 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 public class ExceptionInChildThread implements Runnable { @Override public void run () { throw new RuntimeException ("子线程发生了异常..." ); } private static void exceptionThread () throws InterruptedException { new Thread (new ExceptionInChildThread ()).start(); TimeUnit.MILLISECONDS.sleep(200L ); new Thread (new ExceptionInChildThread ()).start(); TimeUnit.MILLISECONDS.sleep(200L ); } public static void main (String[] args) throws InterruptedException { try { exceptionThread(); } catch (Exception e) { System.out.println("捕获到了异常?" + e.getMessage()); e.printStackTrace(); } } }
实际运行结果:
没有被try catch捕获。
后续的线程没有因为第一个线程发生异常而跳过。
无法在一个线程中通过try catch捕获另外一个线程的异常 。
线程内部捕获
缺点:每个线程都需要编写重复的try catch 代码
1 2 3 4 5 6 7 8 9 @Override public void run () { try { throw new RuntimeException ("子线程发生了异常..." ); } catch (Exception e) { System.out.println("在线程内部捕获异常" +e); } }
线程异常处理器
当一个线程由于未捕获异常而退出时,JVM会把这个事件报告给应用程序提供的UncaughtExceptionHandler异常处理器
源码说明 1 2 3 4 5 6 7 8 9 10 11 12 @FunctionalInterface public interface UncaughtExceptionHandler { void uncaughtException (Thread t, Throwable e) ; }
自定义线程异常处理器 1 2 3 4 5 6 7 8 9 10 11 12 public class CustomThreadUncaughtExceptionHandler implements Thread .UncaughtExceptionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(CustomThreadUncaughtExceptionHandler.class); @Override public void uncaughtException (Thread t, Throwable e) { LOGGER.error("捕获到线程发生的异常,线程信息:[{}]" , JSON.toJSONString(t), e); } }
全局的异常处理器 给所有线程设置统一的异常处理器: 通过调用Thread的静态方法setDefaultUncaughtExceptionHandler()
,设置Thread的静态属性defaultUncaughtExceptionHandler
.为我们自定义的异常处理器。
1 Thread.setDefaultUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());
测试:
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 @Slf4j public class ExceptionInChildThread implements Runnable { @Override public void run () { throw new RuntimeException ("子线程发生了异常..." ); } public static void main (String[] args) throws InterruptedException { Thread.setDefaultUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler ()); exceptionThread(); } private static void exceptionThread () throws InterruptedException { new Thread (new ExceptionInChildThread ()).start(); TimeUnit.MILLISECONDS.sleep(200L ); new Thread (new ExceptionInChildThread ()).start(); TimeUnit.MILLISECONDS.sleep(200L ); } }
特定的异常处理器 给每个线程设置特定的异常处理器: 在Thread类中,还有一个实例属性private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
。通过给这个属性赋值,可以实现为每个线程对象设置不同的异常处理器。
测试使用: 结果: 成功捕获线程1的异常信息
1 2 3 4 5 6 7 8 9 private static void exceptionThread () throws InterruptedException { Thread thread1 = new Thread (new ExceptionInChildThread ()); thread1.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler ()); thread1.start(); TimeUnit.MILLISECONDS.sleep(200L ); new Thread (new ExceptionInChildThread ()).start(); TimeUnit.MILLISECONDS.sleep(200L ); }
线程组 给线程组设置异常处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 ThreadGroup threadGroup = new ThreadGroup ("只知道抛出异常的线程组..." ) { @Override public void uncaughtException (Thread t, Throwable e) { log.error("线程组内捕获到线程[{},{}]异常" , t.getId(), t.getName(), e); } }; ExceptionInThreadGroup exceptionInThreadGroup = new ExceptionInThreadGroup ();new Thread (threadGroup, exceptionInThreadGroup, "线程1" ).start();Thread thread = new Thread (threadGroup, exceptionInThreadGroup, "线程2" );thread.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler ()); thread.start();
线程池异常处理器 因为线程池也是通过new Thread()
的方式创建的线程,所以思想与上面两种方法一致。
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 import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class CatchThreadPoolException { public static void main (String[] args) { ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(5 , r -> { Thread t = new Thread (r); t.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler ()); return t; }); threadPoolExecutor.execute(() -> { throw new RuntimeException ("execute()发生异常" ); } ); threadPoolExecutor.submit(new Runnable () { @Override public void run () { throw new RuntimeException ("submit.run()发生异常" ); } }); threadPoolExecutor.shutdown(); } }
捕获submit任务异常 可以通过Future对象的get()方法来获取任务的执行结果,并在调用get()方法时捕获异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void main (String[] args) { ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(5 , r -> { Thread t = new Thread (r); t.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler ()); return t; }); Future<?> future = threadPoolExecutor.submit(new Runnable () { @Override public void run () { throw new RuntimeException ("submit.run()发生异常" ); } }); try { future.get(); } catch (Exception e) { log.info("future.get()发生异常[{}]" ,e); } finally { threadPoolExecutor.shutdown(); } threadPoolExecutor.shutdown(); }
通过submit()
方法的源码可以发现,submit()
是将runnable()
封装成了RunnableFuture<Void>
,并最终调用execute(ftask);
执行。可以使用ThreadPoolExecutor
的afterExecute()
方法捕获异常:
可以重写afterExecute()
方法来捕获任务执行过程中的异常。
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 @Slf4j public class CustomThreadPool extends ThreadPoolExecutor { public CustomThreadPool (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) { super (corePoolSize, maximumPoolSize, keepAliveTime, unit, new LinkedBlockingDeque <>(1024 ), r -> { Thread t = new Thread (r); t.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler ()); return t; }); } @Override protected void afterExecute (Runnable r, Throwable t) { super .afterExecute(r, t); if (r instanceof FutureTask<?>) { try { ((FutureTask<?>) r).get(); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } catch (ExecutionException e) { log.error("捕获到线程的异常返回值" , e); } } } }
匿名子类的写法
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 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor ( 2 , 4 , 1L , TimeUnit.MINUTES, new LinkedBlockingDeque <>(1024 ), new ThreadFactory () { @Override public Thread newThread (Runnable r) { Thread thread = new Thread (r); thread.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler ()); return thread; } } ) { @Override protected void afterExecute (Runnable r, Throwable t) { super .afterExecute(r, t); if (r instanceof FutureTask<?>) { try { ((FutureTask<?>) r).get(); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } catch (ExecutionException e) { log.error("捕获到线程的异常返回值" , e); } } } };
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 public class CatchThreadPoolException { public static void main (String[] args) { ExecutorService executor = new CustomThreadPool (5 , 10 , 1 , TimeUnit.MINUTES); executor.submit(() -> { throw new RuntimeException ("Task exception" ); }); executor.shutdown(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 2023-06-14 09:57:44 525 Thread-0 ERROR CustomThreadPool (CustomThreadPool.java:37) - 捕获到线程的异常返回值 java.util.concurrent.ExecutionException: java.lang.RuntimeException: Task exception at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:192) at pers.fulsun.CustomThreadPool.afterExecute(CustomThreadPool.java:32) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1157) at java.util.concurrent.ThreadPoolExecutor$Worker .run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:750) Caused by: java.lang.RuntimeException: Task exception at pers.fulsun.CatchThreadPoolException.lambda$main$0 (CatchThreadPoolException.java:15) at java.util.concurrent.FutureTask.run$$$capture (FutureTask.java:266) at java.util.concurrent.FutureTask.run(FutureTask.java) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ... 2 more