线程中出现异常的处理

目录

前言

正文

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

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

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

4.线程组内处理异常

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

总结


前言

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


正文

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

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

创建测试用例

java 复制代码
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() 方法进行异常处理

新建测试用例

java 复制代码
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() 方法进行异常处理

新建测试用例

java 复制代码
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.线程组内处理异常

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

java 复制代码
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();

    }
}

运行结果如图:

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

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

java 复制代码
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 的代码如下:

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 方法如下:

java 复制代码
 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.线程异常处理的优先性

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

创建测试用例:

java 复制代码
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:

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

运行结果如图:

更改 Run1:

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

运行结果如图:

再继续实验,创建 Run2

java 复制代码
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:

java 复制代码
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();来执行 设置的静态方法。源代码如下:

java 复制代码
    //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 代码

java 复制代码
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 方法会被回调。在该方法中,我们可以采取相应的措施来处理异常,比如记录日志或者发送警报等。

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

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

相关推荐
掐指一算乀缺钱19 分钟前
SpringBoot 数据库表结构文档生成
java·数据库·spring boot·后端·spring
晚睡早起₍˄·͈༝·͈˄*₎◞ ̑̑24 分钟前
苍穹外卖学习笔记(七)
java·windows·笔记·学习·mybatis
就这个java爽!30 分钟前
JAVA网络编程【基于TCP和UDP协议】超详细!!!
java·开发语言·网络·tcp/ip·udp·eclipse·idea
一叶飘零_sweeeet34 分钟前
为什么 Feign 要用 HTTP 而不是 RPC?
java·网络协议·http·spring cloud·rpc·feign
懒洋洋大魔王1 小时前
7.Java高级编程 多线程
java·开发语言·jvm
茶馆大橘1 小时前
【黑马点评】已解决java.lang.NullPointerException异常
java·开发语言
星辰@Sea1 小时前
服务注册中心对比及使用场景分析
java·云原生
马剑威(威哥爱编程)1 小时前
除了递归算法,要如何优化实现文件搜索功能
java·开发语言·算法·递归算法·威哥爱编程·memoization
bug菌¹1 小时前
滚雪球学SpringCloud[4.1讲]: Spring Cloud Gateway详解
java·spring cloud·微服务
MuseLss1 小时前
HashMap高频面试知识点
java·开发语言·哈希算法