首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >高并发编程-捕获线程运行时的异常 + 获取调用链

高并发编程-捕获线程运行时的异常 + 获取调用链

作者头像
小小工匠
发布2021-08-17 10:30:04
发布2021-08-17 10:30:04
5640
举报
文章被收录于专栏:小工匠聊架构小工匠聊架构

概述


捕获线程运行时的异常

我们看下Thread的定义 实现了Runnable接口

重写了run方法

根据方法签名可知,run方法是不能向上层抛出异常的,如果线程内部产生异常, 不catch的情况下,上层调用代码如何知道呢?


使用场景

为啥需要这样做呢?

一个线程抛出异常之后,只会在控制台打印堆栈信息,即使有日志记录,因为程序捕获不到异常,只会在控制台打出,并不是在日志记录中出现。

所以,除非在线程抛出异常的时候,你刚好在观察控制台输出的日子,看到了堆栈信息,否则,很难找到线程是哪里抛出了异常。

所以上面我们说到的捕获线程内异常,就有用了,正常情况下,我们捕获不到线程内的异常,但是我们可以通过 UncaughtExceptionHandler 来进行捕获异常。并在在Handler中打印出错误日志,方便定位排查问题。


UncaughtExceptionHandler 接口

先看下 Thread类中的UncaughtExceptionHandler接口


示例

两个线程,一个线程一直运行 ,另外一个线程有异常(一个数组下标越界异常,一个OOM异常 )

这里用OOM来演示

JVM参数设置: -Xms10m -Xmx10m

代码语言:javascript
复制
package com.artisan.test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CaughtThreadExceptionDemo {

    public static void main(String[] args) {
        List list = Arrays.asList(1, 2, 3);

        // 模拟线程一 抛出异常 被终止
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(2_000);
//                list.get(99);

                List list2 = new ArrayList();
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    list2.add(i + "biubiubiubiubiubiubiubiubiubiubiubiu");
                }

            } catch (InterruptedException e) { // 这个地方不要捕获 ArrayIndexOutOfBoundsException ,否则setUncaughtExceptionHandler无法捕获到该异常
                System.out.println(Thread.currentThread().getName() + " some error happened....");
                e.printStackTrace();
            }
        }, "TEST_THREAD_1");

        // 线程启动之前setUncaughtExceptionHandler
        t.setUncaughtExceptionHandler((thread, e) -> {
            System.out.println(" UncaughtExceptionHandler handle...." + e);
            System.out.println(" UncaughtExceptionHandler handle...." + thread.getName());

        });


        t.start();


        // 线程二 一直运行
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(10_000);
                    System.out.println(Thread.currentThread().getName() + " working...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "TEST_THREAD_2").start();
    }
}

输出

注意事项

  1. 要处理的异常,不要被run方法中的catch捕获(如果有catch的话)
  2. setUncaughtExceptionHandler 在 start之前调用

获取调用链

假设线程抛出如上异常,我们想记录下更多的信息到DB或者其他存储介质中,那如何打印出类似上面的信息呢?

答案就是: getStackTrace() ,然后把它的输出获取出来 。

示例如下:

代码语言:javascript
复制
package com.artisan.test;

import java.util.Arrays;
import java.util.Optional;

public class StackTraceDemo {

    public static void main(String[] args) {
        Test1 test1 = new Test1();
        test1.test1();
    }

    static class Test1 {
        public void test1() {
            new Test2().test2();
        }
    }

    static class Test2 {
        public void test2() {

            // Thread.currentThread().getStackTrace() 数组 转 List
            // List  stream ,然后过滤掉本地方法,最后遍历 输出

            Arrays.asList(Thread.currentThread().getStackTrace()).stream()
                    // 过滤掉native方法
                    .filter(element -> !element.isNativeMethod())
                    .forEach(element -> Optional.of(element.getClassName() + ":" + element.getMethodName() + ":" + element.getLineNumber())
                            .ifPresent(System.out::println));
        }
    }
}

输出如下:


使用线程池的场景: 获取线程运行时异常

戳这里

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/10/18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 捕获线程运行时的异常
    • 使用场景
    • UncaughtExceptionHandler 接口
    • 示例
  • 获取调用链
  • 使用线程池的场景: 获取线程运行时异常
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档