线程中出现异常的处理

目录

前言

正文

1.线程出现异常的默认行为 

2.使用 setUncaughtExceptionHandler() 方法进行异常处理

3.使用 setDefaultUncaughtExceptionHandler() 方法进行异常处理 

4.线程组内处理异常 

5.线程异常处理的优先性 

总结


前言

在紧密交织的多线程环境中,异常处理是一个经常被讨论的容易被忽视的关键部分。这并不奇怪,因为在编写并发代码时,管理和理解可能出现的各种异常条件可能是一个挑战。在单线程环境中,发生异常时,异常信息会立刻被捕获并处理,然而,在多线程环境中的异常处理复杂性要高很多。未被正确处理的异常可能导致全局性影响,甚至系统崩溃。这给程序稳定性带来威胁,且可能会导致无法预料的行为。因此,对于编写健壮且可靠的并发代码来说,理解并且正确处理线程中的异常是至关重要的。本文旨在深入探讨Java中线程级别的异常处理。我将会详细阐述线程异常的基本概念,错误处理策略以及如何用实际代码进行演示。希望本文能为你在并发编程中如何捕获和处理异常提供有用的参考。


正文

当线程出现异常时,我们可以在该线程 run() 方法的 catch 语句中进行处理。当有多个线程中出现异常时,我们就得在每一个线程 run() 方法得 catch 语句中进行处理,这样回造成代码严重冗余。我们可以使用 setDefaultUncaughtExceptionHandler() 和 setUncaughtExceptionHandler() 方法来集中处理线程的异常。 

1.线程出现异常的默认行为 

创建测试用例

package org.example.Error;

public class threadCreateException {
    static class MyThread extends Thread{
        @Override
        public void run() {
            String username= null;
            //NUllPointError
            System.out.println(username.hashCode());
        }
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

运行结果

程序运行后,控制台输出空指针异常。在 Java 的多线程技术中,我们可以对多线程中的异常进行"捕捉"(使用的是 UncaughtExceptionHander 接口),从而对异常进行有效处理。

当线程出现异常而终止时,JVM 虚拟机捕获到此情况,并自动调用 UncaughtExceptionHandler 接口中的 void uncaughtException(Thread t,Throwable e) 方法来处理异常,使对多个线程的异常处理更加集中。

2.使用 setUncaughtExceptionHandler() 方法进行异常处理

新建测试用例

package org.example.Error;
import java.lang.Thread.UncaughtExceptionHandler;
public class Main {
    static class MyThread extends Thread{
        @Override
        public void run() {
            String username= null;
            //NUllPointError
            System.out.println(username.hashCode());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread();
        t1.setName(" 线程     t1");
        t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("线程:"+t.getName()+" 出现了异常:");
                e.printStackTrace();
            }
        });
        t1.start();
        MyThread t2 = new MyThread();
        t2.setName(" 线程 t2");
        Thread.sleep(10);
        t2.start();
    }

}

程序运行结果如图

setUncaughtExceptionHandler() 方法的作用是对指定的线程对象设置默认的异常处理器,在 Thread 类中,我们还可以使用 setDefaultUncaughtExceptionHandler() 方法对所有线程对象设置异常处理器。

3.使用 setDefaultUncaughtExceptionHandler() 方法进行异常处理 

新建测试用例 

package org.example.Error;

public class Main3 {
    static class MyThread extends Thread{
        @Override
        public void run() {
            String username= null;
            //NUllPointError
            System.out.println(username.hashCode());
        }
    }
    public static void main(String[] args) {
        MyThread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("线程:"+t.getName()+" 出现了异常:");
                e.printStackTrace();
            }
        });
        MyThread t1 = new MyThread();
        t1.setName(" 线程t1 ");
        t1.start();
        MyThread t2 = new MyThread();
        t2.setName(" 线程t2 ");
        t2.start();

    }
}

运行结果如下

4.线程组内处理异常 

线程的异常处理情况我们已经了解,那么线程组内的异常处理情况又是怎么样的呢? 

package org.example.Error;

public class thread_Group1 {
    static class MyThread extends Thread {
        private String num;

        public MyThread(ThreadGroup group, String name, String num) {
            super(group, name);
            this.num = num;
        }

        @Override
        public void run() {
            int numInt = Integer.parseInt(num);
            while (true) {
                System.out.println("死循环中: " + Thread.currentThread().getName());
            }
        }
    }

    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("我得线程组");
        MyThread[] myThreads = new MyThread[10];
        for (int i = 0; i < myThreads.length; i++) {
            myThreads[i] = new MyThread(group,"线程 "+(i+1),"1");
            myThreads[i].start();
        }
        MyThread newT = new MyThread(group,"报错线程 ","a");
        newT.start();

    }
}

运行结果如图:

从运行结果来看,默认的情况下线程组中的一个线程出现异常后不会影响其他线程的运行。

如何实现线程组内一个线程出现异常后全部线程都停止呢?

class MyThreadGroup extends ThreadGroup {

        public MyThreadGroup(String name) {
            super(name);
        }

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            super.uncaughtException(t, e);
            this.interrupt();
        }
    }

注意,使用 this 关键字停止线程 。this 代表的是线程组!

public void uncaughtException(Thread t, Throwable e)  方法的 t 参数是出现异常的线程对象。

类 MyThread.java 的代码如下: 

    static class MyThread extends Thread {
        private String num;

        public MyThread(ThreadGroup group, String name, String num) {
            super(group, name);
            this.num = num;
        }

        @Override
        public void run() {
            int numInt = Integer.parseInt(num);
            while (true) {
                if (!this.isInterrupted()){
                    System.out.println("死循环中: " + Thread.currentThread().getName());
                }
            }
        }
    }

需要注意的是,使用自定义 java.lang.ThreadGroup 线程组,并重写 uncaughtException() 方法处理组内线程中断行为时,每个线程对象中的 run() 方法内部不要有异常 catch 语句。如果有 catch 语句 ,public void uncaughtException(Thread t, Throwable e) 方法不执行。

main 方法如下:

 public static void main(String[] args) throws InterruptedException {
        MyThreadGroup group = new MyThreadGroup("我的线程组");
        MyThread[] myThreads = new MyThread[10];
        for (int i = 0; i < myThreads.length; i++) {
            myThreads[i] = new MyThread(group," 线程 "+(i+1),"1");
            myThreads[i].start();
        }
        MyThread newT = new MyThread(group,"报错线程","a");
        newT.start();
    }

运行后一个线程出现异常,其他线程全部停止,运行结果如图:

5.线程异常处理的优先性 

前面介绍了若干个线程异常处理方式,这些方式如果一起运行,会出现什么运行结果呢? 

创建测试用例: 


public class threadExceptionMove {
    //线程类
    static class MyThread extends Thread{
        private String num = "a";

        public MyThread() {
        }

        public MyThread(ThreadGroup group, String name) {
            super(group, name);
        }

        @Override
        public void run() {
            int numInt = Integer.parseInt(num);
            System.out.println(" 在线程中打印: "+(numInt+1));
        }
    }
    //线程组的异常处理
    static class MyThreadGroup extends ThreadGroup{
        public MyThreadGroup(String name) {
            super(name);
        }

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            super.uncaughtException(t, e);
            System.out.println("线程组的异常处理");
            e.printStackTrace();
        }
    }
    //对象的异常处理
    static class ObjectUncaughtExceptionHandler implements UncaughtExceptionHandler{

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println(" 对象的异常处理 ");
            e.printStackTrace();
        }
    }
    //静态的异常处理
    static class StateUncaughtExceptionHandler implements UncaughtExceptionHandler{
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("静态的异常处理");
            e.printStackTrace();
        }
    }
}

创建运行类 Run1:

    static class Run1{
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
            //对象
            myThread.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());
            //类
            MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());
            myThread.start();
        }
    }

运行结果如图:

更改 Run1:

static class Run1{
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
            //对象
            //smyThread
            //.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());
            //类
            MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());
            myThread.start();
        }
    }

运行结果如图:

再继续实验,创建 Run2

static class Run2 {
        public static void main(String[] args) {
            MyThreadGroup myThreadGroup = new MyThreadGroup("我的线程组");
            MyThread myThread = new MyThread(myThreadGroup, "我的线程");
            //对象
            myThread
                    .setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());
            //类
            MyThread.
                    setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());
            myThread.start();
        }
    }

运行结果如图:

更改 Run2:

static class Run2 {
        public static void main(String[] args) {
            MyThreadGroup myThreadGroup = new MyThreadGroup("我的线程组");
            MyThread myThread = new MyThread(myThreadGroup, "我的线程");
            //对象
            //myThread
            //        .setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());
            //类
            MyThread.
                    setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());
            myThread.start();
        }
    }

运行结果如图:

本示例想要打印 "静态的异常处理" 信息,必须在  public void uncaughtException(Thread t, Throwable e) 方法中加上 super.uncaughtException(t, e); 代码。这是因为 super.uncaughtException(t, e); 方法中会调用 Thread.getDefaultUncaughtExceptionHandler();来执行 设置的静态方法。源代码如下:

    //super.uncaughtException(t, e);
    public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            //此处
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }

继续更改 Run2 代码

static class Run2 {
        public static void main(String[] args) {
            MyThreadGroup myThreadGroup = new MyThreadGroup("我的线程组");
            MyThread myThread = new MyThread(myThreadGroup, "我的线程");
            //对象
            //myThread
            //        .setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());
            //类
            //MyThread.
            //        setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());
            myThread.start();
        }
    }

运行结果如图:

最终得出结论就是如果调用 setUncaughtExceptionHandler() 方法,则其设置的异常处理器优先运行,其他异常处理器不运行。


总结

当线程出现异常时,如果该异常没有被捕获和处理,线程就会终止。正确处理线程中的异常是保证程序健壮性和稳定性的关键所在。

处理线程中的异常通常有两种方式:一种是使用 try-catch 块捕获异常,第二种是使用线程的未捕获异常处理器(UncaughtExceptionHandler)来处理未捕获的异常。

在使用 try-catch 块捕获异常时,我们可以通过捕获并处理异常来保证程序的稳定性,应该将 catch 块放置在可能出现异常的代码块之后,防止程序因未处理的异常而崩溃。在捕获到异常后,可以在 catch 块中采取相应的措施,比如记录日志或者返回默认值等。

而在使用线程的未捕获异常处理器时,我们需要通过实现 UncaughtExceptionHandler 接口,并将其设置到对应线程上,当线程发生未捕获异常时,处理器中的 uncaughtException 方法会被回调在该方法中,我们可以采取相应的措施来处理异常,比如记录日志或者发送警报等。

当出现异常时,如何选择捕获和处理异常的方式取决于具体情况,但处理异常的目标总是相同的,即保障程序能够继续稳定执行。因此,对于线程中的异常,我们需要充分了解其出现的原因和可能的影响,制定相应的处理策略,并积极监控和记录程序运行时的异常情况。

总之,对线程中出现的异常进行正确处理是保证程序健壮性和稳定性的重要措施,有助于保证程序顺利运行并最大限度地避免非预期的错误。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/215247.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BearPi Std 板从入门到放弃 - 引气入体篇(2)(按键触发外部中断控制LED亮灭)

简介 基于 第一篇文章 的介绍&#xff0c; 我们新增按键的中断控制&#xff1b; 开发板 &#xff1a; Bearpi Std(小熊派标准板) 主芯片: STM32L431RCT6 LED : PC13 \ 推挽输出即可 \ 高电平点亮 KEY1 : PB2 \ 上拉 \ 按下下降沿触发(一次)/上下沿触发(两次&#xff0c;实现…

git常用命令小记

&#xff08;文章正在持续更新中&#xff09; git init - 在当前目录下初始化一个新的 Git 仓库。 git clone [url] - 克隆远程仓库到本地。 git add [file] - 将文件添加到暂存区。 git commit -m "commit message" - 将添加到暂存区的文件提交到本地仓库。 git pus…

python毕业设计论文选题管理系统b615y

毕业论文管理方式效率低下&#xff0c;为了提高效率&#xff0c;特开发了本毕业论文管理系统。本毕业论文管理系统主要实现的功能模块包括学生模块、导师模块和管理员模块三大部分&#xff0c;具体功能分析如下&#xff1a; &#xff08;1&#xff09;导师功能模块&#xff1a;…

Rust UI开发(五):iced中如何进行页面布局(pick_list的使用)?(串口调试助手)

注&#xff1a;此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库&#xff0c;用于为rust语言程序构建UI界面。 这是一个系列博文&#xff0c;本文是第五篇&#xff0c;前四篇链接&#xff1a; 1、Rust UI开发&#xff08;一&#xff09;&#xff1a;使用iced构建UI时…

小红书营销4步骤,品牌需要经历哪些流程?

小红书平台是很多品牌首选的营销“主战场”。想要充分利用平台的优势&#xff0c;做好小红书营销&#xff0c;提升品牌市场认知度&#xff0c;打开产品销路&#xff0c;今天我们为大家分享下小红书营销4步骤&#xff0c;品牌需要经历哪些流程&#xff1f; 一、小红书营销四步法…

uniapp微信小程序解决绘制polygon结束时的问题

目录 一、前言 二、实现思路 三、结束标绘具体代码 1、在地图展示工具栏处判断工具按钮是否展示v-if"item.isshow" 2、data声明的工具按钮中新增结束标绘按钮 3、在按钮的点击事件中新增结束标绘的判断 4、判断绘制的线段个数是否大于等于三条&#xff0c;当满…

qiankun: 关于ElementUI字体图标加载不出来的问题

问题描述&#xff1a; 子应用使用的是vueelementUI&#xff0c;在项目main.js中需要引入elementUI的样式文件。elementUI的样式文件中有字体文件的引用&#xff0c;是以相对路径的形式写在css文件中的&#xff0c; 本来独立部署项目访问是没问题的&#xff0c;问题出现在以qi…

(03)vite 处理 css

文章目录 系列全集vite 处理css流程vite如何解决协同开发&#xff0c;样式重复覆盖的问题&#xff1f;使用less通过配置&#xff0c;更改vite的css默认行为 系列全集 &#xff08;01&#xff09;vite 从启动服务器开始 &#xff08;02&#xff09;vite环境变量配置 &#xff…

DevEco Studio设置背景图片

我们打开编辑器 左上角菜单 选择 File 下的 Settings 我们选择首选项 Appearance 8 Behavior 下的 Appearance 右侧界面 点击下面的 Background lmage… 新弹出的这个窗口 我们可以鼠标 拖拽它的边 把他拉大一点 当前 我们代码中是没有背景图片的 我们点击入下图指向的三个…

凌恩生物福利第二弹:转发送书!全方位多组学研究解决方案!

研究微生物的组学有很多种&#xff0c;都能在研究中提供研究所需的信息&#xff0c;然而随着科学研究的发展过程&#xff0c;单一的微生物组学研究已很难系统地阐释其生物过程的发生机制与发展变化&#xff0c;微生物与宿主&#xff0c;环境之间的整体性和复杂性&#xff0c;需…

centos安装Python3之后yum不能使用异常

场景&#xff1a; 需要在centos上安装Python3&#xff0c;但是安装Python3之后出现yum不能使用的问题。 问题描述 在centos上安装python3之后出现yum使用不了问题&#xff0c;使用yum会报如下信息&#xff1a; [roothadoop101~]# yum install wgetFile "/usr/bin/yum&q…

Python Rich模块:打造绚丽多彩的命令行界面

概要 在Python编程领域&#xff0c;命令行界面是开发者经常接触的环境之一。然而&#xff0c;传统的命令行界面通常只能呈现简单的文本信息&#xff0c;难以展现丰富的数据和交互效果。幸运的是&#xff0c;有一款名为Rich的Python模块&#xff0c;它能够让我们在命令行界面中创…

深度学习算法:探索人工智能的前沿

目录 引言 第一部分&#xff1a;深度学习的基础 1.1 什么是深度学习&#xff1f; 1.2 神经网络的演化 第二部分&#xff1a;深度学习的关键技术 2.1 卷积神经网络&#xff08;CNN&#xff09; 2.2 循环神经网络&#xff08;RNN&#xff09; 2.3 长短时记忆网络&#xf…

陶博士月线反转6.4 python 化代码

陶博士月线反转6.4 python 化代码 量化系统有好几个月没有进度了&#xff0c;之前一直纠结策略问题&#xff0c;无从下手。最近和量化的同学聊了下&#xff0c;还得先把自动交易流程先跑起来后面再慢慢优化策略。 所以先拿陶博士的月线反转6.4 python 化&#xff0c;作为试水的…

LVS-DR+Keepalived+动静分离实验

架构图 解释一下架构&#xff0c;大概就是用Keepalived实现两台DR服务器的LVS负载均衡&#xff0c;然后后端服务器是两台Nginx服务器两台Tomcat服务器并且实现动静分离这个实验其实就是把 LVS-DRKeepalived 和 动静分离 给拼起来&#xff0c;真的是拼起来&#xff0c;两个部分…

【工具使用-Audition】如何使用Audition查看频率

一&#xff0c;简介 在工作过程中要对处理后的音频进行频率分析&#xff0c;本文以Audition 2020为例进行说明&#xff0c;供参考。 二&#xff0c;操作步骤 2.1 生成测试音源 使用Audition生成左通道为1KHz&#xff0c;右通道为10KHz的音源信号 如图所示&#xff1a; 2.…

UE学习C++(1)创建actor

创建新C类 在 虚幻编辑器 中&#xff0c;点击 文件&#xff08;File&#xff09; 下拉菜单&#xff0c;然后选择 新建C类...&#xff08;New C Class...&#xff09; 命令&#xff1a; 此时将显示 选择父类&#xff08;Choose Parent Class&#xff09; 菜单。可以选择要扩展的…

学习-java多线程面试题

为什么不建议用Executors启动线程池 *队列LinkedBlockingQueue是没有边界的队列,请求多会造成OOM *建议使用ThreadPoolExecutors 线程池中提交一个任务的流程&#xff1f; 1先判断线程池是否有线程&#xff0c;如果与就直接执行&#xff0c;没有就放队列 2如果队列满了&#…

kubeadm 安装k8s1.28.x 底层走containerd 容器

1. k8s1.28.x 的概述 1.1 k8s 1.28.x 更新 Kubernetes v1.28 是 2023 年的第二个大版本更新&#xff0c;包含了 46 项主要的更新。 而今年发布的第一个版本 v1.27 有近 60 项&#xff0c;所以可以看出来&#xff0c;在发布节奏调整后&#xff0c; 每个 Kubernetes 版本中都会包…

使用 .NET Upgrade Assistant(升级助手)升级 .NET 老旧版本项目

使用 .NET Upgrade Assistant 升级 .NET 老旧版本项目 .NET Upgrade Assistant 概述.NET Upgrade Assistant 功能1、支持以下代码语言&#xff1a;2、支持的 .NET 升级路径&#xff1a;3、支持的项目类型&#xff1a; .NET Upgrade Assistant 安装1、使用 Visual Studio 扩展安…