1. 引子
在 java 多线程并发编程中, 有八大基础核心.
看看都有哪八大基础核心呢? 它们分别是:
1. 创建线程的方式
2. 线程启动
3. 线程停止
4. 线程生命周期
5. 线程相关的方法
6. 线程相关的属性
7. 线程异常处理
8. 线程安全
今天我们从第七个基础核心开始: 线程异常处理
2. 考考你
# 前情回顾
在软件项目开发中, 除了要处理正常的业务流程外, 异常处理也是我们绕不过去的一个坎
# 考考你
1. 你知道 java 的异常体系吗?
2. 你知道哪一种异常处理方式比较好吗?
3. 你知道如何使用 UncaughtExceptionHandler 吗?
3. 案例
3.1. 难以发现的子线程异常
简述:
1. 在子线程 child-exception-0 中, 抛出异常
2. 主线程 main 依然正常执行, 在实际项目中, 会导致难以发现子线程的异常情况
- package com.anan.thread.threadexception;
- /**
- * 主线程 main 不能发现子线程异常
- */
- public class NotFoundChildThreadException {
- public static void main(String[] args) {
- // 创建线程对象
- Runnable r1 = new MyRunnable();
- Thread t1 = new Thread(r1,"child-exception-0");
- t1.start();
- // 主线程循环打印输出, 忽略子线程异常
- for (int i = 0; i < 5; i++) {
- System.out.println("主线程 main 输出: 风景这边独好! 当前索引 ["
- + i + "]");
- }
- }
- }
- /**
- * 实现 Runnable, 创建线程
- */
- class MyRunnable implements Runnable{
- public void run() {
- System.out.println(Thread.currentThread().getName() +
- "准备抛出异常了...start");
- // 抛出异常
- throw new RuntimeException("子线程抛出了异常.");
- }
- }
执行结果:
3.2. 不能捕获的子线程异常
简述:
1. 创建 3 个子线程: thread-0,thread-1,thread-2
2. 每个子线程都会抛出异常
3. 在主线程 main 中, 进行捕获处理. 期望如果 thread-0 抛出了异常, 那么 thread-1/thread-2 线程, 不要创建执行
- /**
- * 不能捕获的子线程异常
- */
- public class NotCaughtChildException {
- public static void main(String[] args) {
- // 预期子线程会抛出异常, 通过 try{}catch(){} 捕获处理
- try{
- // 创建子线程 0
- Runnable r1 = new MyRunnable1();
- Thread t0 = new Thread(r1,"thread-0");
- t0.start();
- // 创建子线程 1
- Thread t1 = new Thread(r1,"thread-1");
- t1.start();
- // 创建子线程 2
- Thread t2 = new Thread(r1,"thread-2");
- t2.start();
- }catch (RuntimeException e){
- System.out.println("捕获到了异常.");
- }
- }
- }
- /**
- * 实现 Runnable, 创建线程
- */
- class MyRunnable1 implements Runnable{
- public void run() {
- System.out.println(Thread.currentThread().getName() +
- "准备抛出异常了...start");
- // 抛出异常
- throw new RuntimeException("子线程抛出了异常.");
- }
- }
执行结果:
3.3. 全局异常处理
简述:
1. 在 3.2. 中, 不能通过 try{}catch(){} 捕获子线程异常. 因为 try{}catch(){} 只能捕获当前线程的异常
2. 如果要对所有子线程, 进行统一异常处理, 需要一个全局异常处理器
3. 全局异常处理器接口: Thread.UncaughtExceptionHandler
4. 实现方式:
4.1. 编写全局异常处理器, 实现接口: Thread.UncaughtExceptionHandler
4.2. 注册使用全局异常处理器
3.3.1. 全局异常处理器
- package com.anan.thread.threadexception;
- /**
- * 自定义全局异常处理器类
- */
- public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
- public void uncaughtException(Thread t, Throwable e) {
- System.out.println(t.getName() + "发生了异常, 异常消息:" + e.getMessage());
- }
- }
3.3.2. 注册使用全局异常处理器
- package com.anan.thread.threadexception;
- /**
- * 注册使用自定义全局异常处理器
- */
- public class UseUncaughtExceptionHandler {
- public static void main(String[] args) {
- // 关键代码: 设置全局异常处理器
- Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
- // 创建子线程 0
- Runnable r1 = new MyRunnable1();
- Thread t0 = new Thread(r1,"thread-0");
- t0.start();
- // 创建子线程 1
- Thread t1 = new Thread(r1,"thread-1");
- t1.start();
- // 创建子线程 2
- Thread t2 = new Thread(r1,"thread-2");
- t2.start();
- }
- }
- /**
- * 实现 Runnable, 创建线程
- */
- class MyRunnable2 implements Runnable{
- public void run() {
- System.out.println(Thread.currentThread().getName() +
- "准备抛出异常了...start");
- // 抛出异常
- throw new RuntimeException("子线程抛出了异常.");
- }
- }
3.3.3. 执行结果
4. 讨论分享
# 考考你答案
1. 你知道 java 的异常体系吗?
1.1.java 异常体系中, 老祖宗是 Throwable
1.2. 在 Throwable 下, 有 Exception 异常体系 (日常开发中, 见得最多)
1.3. 在 Throwable 下, 有 Error 错误体系 (日常开发中, 较少关注)
2. 你知道哪一种异常处理方式比较好吗?
2.1. 通过案例演示, 我们知道不能通过 try{}catch(){}, 跨线程捕获异常. try{}catch(){} 只能捕获当前线程自己的异常
2.2. 处理方式一:
2.2.1. 可以在当前线程中进行 try{}catch(){}, 捕获处理当前线程的异常
2.2.2. 该种方式处理起来代码量多, 繁琐. 不推荐使用
2.3. 处理方式二:
2.3.1. 通过设置全局异常处理器: UncaughtExceptionHandler
2.3.2. 实现异常全局统一处理. 推荐使用
3. 你知道如何使用 UncaughtExceptionHandler 吗?
3.1. 编写全局异常处理器, 实现接口:
Thread.UncaughtExceptionHandler
3.2. 注册使用全局异常处理器:
- // 关键代码: 设置全局异常处理器
- Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
java 异常体系类图:
来源: https://www.cnblogs.com/itall/p/12315722.html