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

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

20191031000749801.png


概述


网络异常,图片无法展示
|


捕获线程运行时的异常


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


20191018162720919.png


重写了run方法


20191018162656758.png


20191018162743618.png


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


使用场景


为啥需要这样做呢?


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


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


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


UncaughtExceptionHandler 接口


先看下 Thread类中的UncaughtExceptionHandler接口


20191018163142122.png

示例


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

这里用OOM来演示

JVM参数设置: -Xms10m -Xmx10m


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();
    }
}


输出


20191018163543666.png

注意事项

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

获取调用链

20191018164332482.png

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

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

示例如下:

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));
        }
    }
}


输出如下:


20191018164627106.png


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

戳这里

相关文章
|
2月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
152 6
|
5月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
306 83
|
5月前
|
人工智能 安全 调度
Python并发编程之线程同步详解
并发编程在Python中至关重要,线程同步确保多线程程序正确运行。本文详解线程同步机制,包括互斥锁、信号量、事件、条件变量和队列,探讨全局解释器锁(GIL)的影响及解决线程同步问题的最佳实践,如避免全局变量、使用线程安全数据结构、精细化锁的使用等。通过示例代码帮助开发者理解并提升多线程程序的性能与可靠性。
189 0
|
7月前
|
Java 开发者 Kotlin
华为仓颉语言初识:并发编程之线程的基本使用
本文详细介绍了仓颉语言中线程的基本使用,包括线程创建(通过`spawn`关键字)、线程名称设置、线程执行控制(使用`get`方法阻塞主线程以获取子线程结果)以及线程取消(通过`cancel()`方法)。文章还指出仓颉线程与Java等语言的差异,例如默认不提供线程名称。掌握这些内容有助于开发者高效处理并发任务,提升程序性能。
247 2
|
2月前
|
Java 测试技术 API
【JUC】(1)带你重新认识进程与线程!!让你深层次了解线程运行的睡眠与打断!!
JUC是什么?你可以说它就是研究Java方面的并发过程。本篇是JUC专栏的第一章!带你了解并行与并发、线程与程序、线程的启动与休眠、打断和等待!全是干货!快快快!
456 2
|
2月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
271 0
|
3月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
239 16
|
3月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
|
7月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
259 0
|
6月前
|
机器学习/深度学习 监控 算法
局域网行为监控软件 C# 多线程数据包捕获算法:基于 KMP 模式匹配的内容分析优化方案探索
本文探讨了一种结合KMP算法的多线程数据包捕获与分析方案,用于局域网行为监控。通过C#实现,该系统可高效检测敏感内容、管理URL访问、分析协议及审计日志。实验表明,相较于传统算法,KMP在处理大规模网络流量时效率显著提升。未来可在算法优化、多模式匹配及机器学习等领域进一步研究。
183 0

热门文章

最新文章