UncaughtExceptionHandler—处理Runnable线程内的非受检异常

Java入门的多线程方案就是Thread类和Runnable接口。如下:

public class Demo {
    public static void main(String[] args) {
        fun1();
        fun2();
    }
 
    public static void fun1() {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("fun1-thread");
            }
        };
        thread.start();
    }
 
    public static void fun2() {
        new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("fun2-thread");
			}
        }).start();
    }
}

今天我们要说的是线程内部的异常处理。Java的异常(包括Exception和Error)分为检查异常(checked exceptions)和非检查的异常(unchecked exceptions),很好理解:

  • 检查异常:编译器要求必须处理的异常,代码在运行前,编译器要求你必须要对这段代码try...catch,或者throws exception,这类这就是检查异常。
  • 非检查异常:只有在运行期才能检查出来的异常,比如NullpointException...

由于在多线程中,run()方法无法继续向上显式抛出异常,所以在线程内部的异常我们必须处理,否则异常会被JVM捕获到,这时JVM就会将线程杀掉。线程内部该如何处理异常呢?

  • 对于受检异常:就是直接在run()方法体中try-catch异常,然后进行对应的处理;
  • 对于非受检异常:虽然编译期不做检查,但仍然可以使用try-catch处理;如果没有try-catch,JVM会帮助我们捕获到异常。
new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("thread start...");
				try {
					int a = 1/0;//uncheck exception
				} catch (Exception e) {
					System.out.println("uncheck exception...");
				}
                try {
					Thread.sleep(10000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
                System.out.println("after sleep 10 ms,thread end...");
			}
        }).start();
//输出:
thread start...
uncheck exception...
after sleep 10 ms,thread end...

上面代码int a = 1/0 是运行期异常,如果用try-catch处理,后面的代码还可以执行,否则抛出异常被JVM捕获,后面的sleep代码就无法执行到。

我们的代码中不可能对所有的运行时异常(unchecked exception)做处理,所以线程中如果未处理的unchecked exception被抛出,那么我们如何处理JVM捕获的异常呢?答案是Thread.UncaughtExceptionHandler类。正如JDK文档所介绍的一样:

“当一个线程由于发生了非受检异常而终止时,JVM会使用Thread.gerUncaughtExceptionHandler()方法查看该线程上的UncaughtExceptionHandler,并调用他的uncaughtException()方法”。

下面我们来尝试使用hread.UncaughtExceptionHandler类,来处理线程内部的非受检异常(受检异常直接在run()方法体内部的catch子句中处理)。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
 

public class Demo {
 
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
			@Override
			public void run() {
				//设置非受检异常的ExceptionHandler
				Thread.currentThread().setUncaughtExceptionHandler(new ThreadExceptionHandler());
				System.out.println("thread start...");
				int a = 1/0;//unchecked exception
                try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
                System.out.println("after sleep 10 ms,thread end...");
			}
        }).start();
    }
 
    private static final class ThreadExceptionHandler implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("One uncaught exception was got:");
            System.out.println("Thread id=" + t.getId());
            System.out.println("Thread name=" + t.getName());
            e.printStackTrace(System.out);
        }
    }
}
//输出
thread start...
One uncaught exception was got:
Thread id=10
Thread name=Thread-0
java.lang.ArithmeticException: / by zero
	at com.iqiyi.toutiao.Test.ThreadTest$2.run(ThreadTest.java:92)
	at java.lang.Thread.run(Thread.java:748)

在线程代码中如果是受检异常,直接在run()方法体中处理;对于非受检异常,我们在run()方法体的开头设置了非受检异常处理类ThreadExceptionException,这个类的uncaughtException()方法就是处理线程内部非捕获异常的具体执行者。(读者要清楚,一旦一个线程抛出了非受检异常,JVM就会把它杀死,然后把捕获到的非受检异常传递给UncaughtExceptionHandler类对象类处理)。

一定有读者会问,如果没有通过setUncaughtExceptionHandler()方法设置Handler怎么办?这个问题在JDK API中给了回答:
“如果一个线程没有显式的设置它的UncaughtExceptionHandler,JVM就会检查该线程所在的线程组是否设置了UncaughtExceptionHandler,如果已经设置,就是用该UncaughtExceptionHandler;否则查看是否在Thread层面通过静态方法setDefaultUncaughtExceptionHandler()设置了UncaughtExceptionHandler,如果已经设置就是用该UncaughtExceptionHandler;如果上述都没有找到,JVM会在对应的console中打印异常的堆栈信息。”

/**
 * Created by yizhen on 2017/1/7.
 */
public class Demo {
 
    public static void main(String[] args) throws InterruptedException {
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("static thread exception handler -- " + t.getName());
            }
        });
        final Thread t1 = new Thread(new ThreadTaskWithHandler(), "t1");
        t1.start();
 
        final Thread t2 = new Thread(new ThreadTaskNoHandler(), "t2");
        t2.start();
 
    }
 
    private static final class ThreadExceptionHandler implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("explicit exception handler -- " + t.getName());
        }
    }
 
    private static final class ThreadTaskWithHandler implements Runnable {
 
        @Override
        public void run() {
            Thread.currentThread().setUncaughtExceptionHandler(new ThreadExceptionHandler());
            System.out.println(12 / 0);
        }
    }
 
    private static final class ThreadTaskNoHandler implements Runnable {
        @Override
        public void run() {
            System.out.println(12 / 0);
        }
    }
}

从上面的程序可以看到,t1线程的非受检异常始终会被explicit exception handler捕获到,而t2线程的非受检异常始终会被static thread exception handler捕获到。
至此,Thread线程内的异常处理就介绍完了,这包括受检异常和非受检异常。细心的读者会发现,本文仅仅涉及Thread和Runnable的多线程体系。在Java中,还有Executor和Callable的多线程体系。那关于这个体系的异常如何处理呢?笔者会后续博文中介绍!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值