1. 介绍
线程不允许抛出未捕获的checked exception
也就是说各个线程需要自己把自己的checked exception处理掉
JVM的这种设计源自于这样一种理念:“线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。”基于这样的设计理念,在Java中,线程方法的异常(无论是checked还是unchecked exception),都应该在线程代码边界之内(run方法内)进行try catch并处理掉。换句话说, 我们不能捕获从线程中逃逸的异常。
2. 未捕获的异常哪去了
一个异常被抛出后,如果没有被捕获处理,则会一直向上抛。
异常一旦被Thread.run() 抛出后,就不能在程序中对异常进行捕获,最终只能由JVM捕获。
2.1. 不处理异常
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
int i = 1 / 0;//发生异常
}
}).start();
}
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at thread.thread.thread.Uncaught$1.run
at java.lang.Thread.run
2.2. 尝试对异常进行捕获
public static void main(String[] args) {
try {
new Thread(new Runnable() {
@Override
public void run() {
int i = 1 / 0;//发生异常
}
}).start();
} catch (Exception e) {
System.out.println("捕获线程抛出的异常!");
}
}
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at thread.thread.thread.Uncaught$1.run
at java.lang.Thread.run
3. JVM如何处理线程中抛出的异常
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) { // 父级优先处理
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) { // 没有配置handler时,默认直接打印到控制台
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
- 查看Thread类的源码,我们可以看到有个dispatchUncaughtException方法,此方法就是用来处理线程中抛出的异常的。JVM会调用dispatchUncaughtException方法来寻找异常处理(UncaughtExceptionHandler),处理异常。
- UncaughtExceptionHandler必须显示的设置,否则默认为null。若为null,则使用线程默认的handler,即该线程所属的ThreadGroup。ThreadGroup自身就是一个handler,查看ThreadGroup的源码就可以发现,ThreadGroup实现了Thread.UncaughtExceptionHandler接口,并实现了默认的处理方法。默认的未捕获异常处理器处理时,会调用 System.err 进行输出,也就是直接打印到控制台了。
4. 为线程 设置异常handle
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private final String name;
public MyUncaughtExceptionHandler(String name) {
this.name = name;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.WARNING, "线程异常,终止啦" + t.getName());
e.printStackTrace();
System.out.println(name + "捕获了异常" + t.getName() + "异常");
}
}
Thread thread = new Thread(() -> {
try {
int i = 1 / 0;
} catch (Exception e) {
throw new RuntimeException("参数错误", e);
}
});
thread.setUncaughtExceptionHandler( new MyUncaughtExceptionHandler("数据处理"));
thread.start();
1月 07, 2023 11:00:02 上午 com.du.exception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦Thread-0
java.lang.RuntimeException: 参数错误
5. 为所有的线程设置
Thread的静态方法 setDefaultUncaughtExceptionHandler
6. future
并不是说一产生异常就抛出,直到我们get执行时,才会抛出
7. 线程组
public class ThreadGroupDemo {
public static class MyThreadGroup extends ThreadGroup{
public MyThreadGroup(String name) {
super(name);
}
//重写uncaughtException方法
@Override
public void uncaughtException(Thread t, Throwable e) {
//以下代码为捕获到异常之后的处理
//super.uncaughtException(t, e);
System.out.println("捕获到抛出异常线程:" + t.getName());
System.out.println("捕获到异常信息:" + e.getMessage());
//如果捕获到线程组内线程的异常,则终止线程组内所有线程
this.interrupt();
}
}
public static class MyThread extends Thread{
private String num;
public MyThread(ThreadGroup threadGroup, String name, String num) {
super(threadGroup, name);
this.num = num;
}
@Override
public void run() {
try {
int numInt = Integer.valueOf(num);
System.out.println("死循环中"+Thread.currentThread().getName());
while (!Thread.currentThread().isInterrupted()){
}
System.out.println("死循环结束"+Thread.currentThread().getName());
} catch (Exception e) {
//抛出运行时异常:子线程抛出异常
throw new RuntimeException(Thread.currentThread().getName()+"子线程抛出异常");
}
}
}
public static void main(String[] args) {
MyThreadGroup myThreadGroup = new MyThreadGroup("我的线程组");
MyThread[] myThreads = new MyThread[5];
for(int i = 0; i<5; i++){
MyThread myThread = new MyThread(myThreadGroup,"线程"+i,String.valueOf(i));
myThreads[i] = myThread;
}
for(MyThread myThread : myThreads){
myThread.start();
}
//唯一的异常线程:我是异常线程
// MyThread myThread1 = new MyThread(myThreadGroup,"我是异常线程","a");
// myThread1.start();
}
}