线程中处理异常是个头疼的问题,在异步的代码中,如何将异常捕获。
捕获异常后,将异常反馈给开发者,或最终用户。
一、将异常反馈给开发者,一般来说,在日志中打印异常日志即可;
二、将异常反馈给最终用户,一般来说,在页面上弹出提示框即可。
将异常打印到日志中(UncaughtExceptionHandler方式处理异常)
在线程当中遇到异常,需要捕捉并打印日志,我们可以考虑使用UncaughtExceptionHandler的回调。
以下是一个简单的测试例子。
有一个线程,方法体中会抛出异常
package com.nicchagil.study.thread.thinking.No15线程异常的捕捉; public class ExceptionThread implements Runnable { @Override public void run() { throw new NullPointerException("故意抛出的异常。"); } }
自定义的异常处理器,这里所做的操作仅是在控制台打印异常,告诉开发者(并非用户哦)有这么个异常发生
package com.nicchagil.study.thread.thinking.No15线程异常的捕捉; import java.lang.Thread.UncaughtExceptionHandler; public class MyUncaughtExceptionHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable throwable) { System.out.println("Print exception @ MyUncaughtExceptionHandler -> " + throwable); } }
为设置方便,通常可以有一个线程工厂(当然也可以直接设置,不通过工厂了),已帮忙批量以通用的参数设置产生线程。见调用类可见,有触发方法使用到此工厂,也有直接实例化线程的。
package com.nicchagil.study.thread.thinking.No15线程异常的捕捉; import java.util.concurrent.ThreadFactory; public class HandlerThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); return t; } }
触发类,这里介绍两种设置方式(一直接设置;一通过工厂设置),还有一种错误的设置方式,以供借镜。
package com.nicchagil.study.thread.thinking.No15线程异常的捕捉; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Call { public static void main(String[] args) { /* Work */ callByWay1(); /* Work */ callByWay2(); /* Un-work */ callByWay3(); } /** * 原始的调用方式,在Thread对象设置异常处理器 */ public static void callByWay1() { try { Thread t = new Thread(new ExceptionThread()); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); t.start(); } catch (Throwable t) { System.out.println("Print exception @ Call.java -> " + t); } } /** * 使用工厂设置异常处理器的调用方式 */ public static void callByWay2() { try { ExecutorService es = Executors.newCachedThreadPool(new HandlerThreadFactory()); es.execute(new ExceptionThread()); } catch (Throwable t) { System.out.println("Print exception @ Call.java -> " + t); } } /** * 实例化Thread后,传入ExecutorService调用。(此方法不行,原因见代码内注释) */ public static void callByWay3() { try { Thread t = new Thread(new ExceptionThread()); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); ExecutorService es = Executors.newCachedThreadPool(); /* 此方法不行,异常处理器虽在前面设置,但由于es.execute(t)的入参为Runnable,故异常处理器遗失 */ es.execute(t); } catch (Throwable t) { System.out.println("Print exception @ Call.java -> " + t); } } }
日志如下,可见:
- 如果正确设置了异常处理器,将会触发异常处理器。我们的异常处理器中书写的逻辑是打印日志,所以,日志打印出来了。
- 无论有无设置异常处理器,异常都不会被直接的catch块捕捉和打印
Print exception @ MyUncaughtExceptionHandler -> java.lang.NullPointerException: 故意抛出的异常。 Print exception @ MyUncaughtExceptionHandler -> java.lang.NullPointerException: 故意抛出的异常。 Exception in thread "pool-1-thread-1" java.lang.NullPointerException: 故意抛出的异常。 at com.nicchagil.study.thread.thinking.No15线程异常的捕捉.ExceptionThread.run(ExceptionThread.java:7) at java.lang.Thread.run(Thread.java:619) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619)
将异常提示给最终用户(捕获异常)
告诉最终用户有异常发生,则需要捕获线程执行过程中产生的异常,捕获异常后传递给页面进行提示。
执行线程,并捕获异常:
import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class MyThread implements Callable<String> { @Override public String call() throws Exception { if (1 == 1) { throw new RuntimeException("模拟的一个异常"); } return "hello world"; } public static void main(String[] args) { ExecutorService es = Executors.newCachedThreadPool(); try { Future<String> f = es.submit(new MyThread()); System.out.println(f.get()); } catch (Throwable t) { System.out.println("捕获异常 : " + t.getMessage()); } } }
日志:
看到“捕获异常 : java.lang.RuntimeException: 模拟的一个异常”就知道异常被捕捉了,剩下的就看你如何传到JSP了,哈哈哈。
捕获异常 : java.lang.RuntimeException: 模拟的一个异常
Runnable的线程也是可以的:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class MyThread implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + " run."); throw new RuntimeException("模拟的一个异常"); } public static void main(String[] args) { ExecutorService es = Executors.newCachedThreadPool(); try { Future<?> f = es.submit(new MyThread()); System.out.println(f.get()); } catch (Throwable t) { System.out.println("捕获异常 : " + t.getMessage()); } } }