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

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

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


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

戳这里

相关文章
|
5天前
|
安全 算法 Java
Java并发编程中的线程安全与性能优化
在Java应用程序开发中,线程安全和性能优化是至关重要的方面。本文探讨了Java并发编程中常见的线程安全问题,并提供了实用的性能优化技巧。通过深入分析多线程环境下的共享资源访问、锁机制、并发集合等关键概念,帮助开发者有效提升程序的稳定性和执行效率。
42 15
|
1天前
|
Java 数据处理 调度
Java多线程编程入门指南
Java多线程编程入门指南
|
5天前
|
Java 开发者
Java并发编程:解锁多线程同步之谜
【6月更文挑战第24天】在Java的世界里,并发编程是提升应用性能和响应能力的关键。本文将深入探讨Java中的多线程同步机制,从基础的synchronized关键字到高级的Lock接口,揭示它们背后的原理与应用场景。我们将通过具体示例,展现如何优雅地处理线程间的竞争条件,确保数据的一致性和完整性。无论你是Java新手还是资深开发者,这篇文章都将为你揭开多线程同步的神秘面纱,让你的代码在多线程环境中运行得更加顺畅。
|
2天前
|
Java 调度
Java多线程编程与并发控制策略
Java多线程编程与并发控制策略
|
2天前
|
安全 Java 开发者
Java并发编程:理解并发与多线程
在当今软件开发领域,Java作为一种广泛应用的编程语言,其并发编程能力显得尤为重要。本文将深入探讨Java中的并发编程概念,包括多线程基础、线程安全、并发工具类等内容,帮助开发者更好地理解和应用Java中的并发特性。
6 1
|
4天前
|
存储 设计模式 安全
C++一分钟之-并发编程基础:线程与std::thread
【6月更文挑战第26天】C++11的`std::thread`简化了多线程编程,允许并发执行任务以提升效率。文中介绍了创建线程的基本方法,包括使用函数和lambda表达式,并强调了数据竞争、线程生命周期管理及异常安全等关键问题。通过示例展示了如何用互斥锁避免数据竞争,还提及了线程属性定制、线程局部存储和同步工具。理解并发编程的挑战与解决方案是提升程序性能的关键。
25 3
|
4天前
|
监控 Java 调度
Java并发编程:深入理解线程池
【6月更文挑战第26天】在Java并发编程的世界中,线程池是提升应用性能、优化资源管理的关键组件。本文将深入探讨线程池的内部机制,从核心概念到实际应用,揭示如何有效利用线程池来处理并发任务,同时避免常见的陷阱和错误实践。通过实例分析,我们将了解线程池配置的策略和对性能的影响,以及如何监控和维护线程池的健康状况。
7 1
|
4天前
|
数据采集 Java Unix
10-多线程、多进程和线程池编程(2)
10-多线程、多进程和线程池编程
|
4天前
|
安全 Java 调度
10-多线程、多进程和线程池编程(1)
10-多线程、多进程和线程池编程
|
1天前
|
安全 Java 数据处理
Android多线程编程实践与优化技巧
Android多线程编程实践与优化技巧