线程中处理异常是个头疼的问题,在异步的代码中,如何将异常捕获。
捕获异常后,将异常反馈给开发者,或最终用户。
一、将异常反馈给开发者,一般来说,在日志中打印异常日志即可;
二、将异常反馈给最终用户,一般来说,在页面上弹出提示框即可。
将异常打印到日志中(UncaughtExceptionHandler方式处理异常)
在线程当中遇到异常,需要捕捉并打印日志,我们可以考虑使用UncaughtExceptionHandler的回调。
以下是一个简单的测试例子。
有一个线程,方法体中会抛出异常
package com.nicchagil.study.thread.thinking.No15线程异常的捕捉;
public class ExceptionThread implements Runnable {
@Override
public void run() {
throw new NullPointerException("故意抛出的异常。");
}
}
View Code
自定义的异常处理器,这里所做的操作仅是在控制台打印异常,告诉开发者(并非用户哦)有这么个异常发生
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);
}
}
View Code
为设置方便,通常可以有一个线程工厂(当然也可以直接设置,不通过工厂了),已帮忙批量以通用的参数设置产生线程。见调用类可见,有触发方法使用到此工厂,也有直接实例化线程的。
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;
}
}
View Code
触发类,这里介绍两种设置方式(一直接设置;一通过工厂设置),还有一种错误的设置方式,以供借镜。
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);
}
}
}
View Code
日志如下,可见:
- 如果正确设置了异常处理器,将会触发异常处理器。我们的异常处理器中书写的逻辑是打印日志,所以,日志打印出来了。
- 无论有无设置异常处理器,异常都不会被直接的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());
}
}
}
View Code
日志:
看到“捕获异常 : 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());
}
}
}
View Code