Day277.线程的各个属性多线程未捕获异常处理 -Juc

Posted 阿昌喜欢吃黄桃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day277.线程的各个属性多线程未捕获异常处理 -Juc相关的知识,希望对你有一定的参考价值。

①线程的各个属性

一、学习前思考的问题

  • 什么时候我们需要设置守护线程?
  • 应该如何应用线程优先级来帮助线程运行?有哪些禁忌?
  • 不同操作系统如何处理优先级问题?

二、线程各属性纵览

image-20210524201910522


三、线程ID

  • 每个线程都有id,这个id不能修改
  • 线程id会不停的自增,从1开始
  • main函数就是第一个线程
  • id是操作系统用来识别各个线程的编号
/******
 @author 阿昌
 @create 2021-05-24 20:26
 *******
 * 线程Id从1开始,JVM运行起来后,自己创建的线程的Id早已不是0
 */
public class Id {
    public static void main(String[] args) {
        Thread thread = new Thread();
        System.out.println("主线程Id:"+Thread.currentThread().getId());
        System.out.println("子线程Id:"+thread.getId());
    }
}

image-20210524202907826

以上看到主线程的id为1,可以理解,那么为什么我们创建的子线程id就直接为12了???

我们打断点调试发现,JVM其实已经为我们创建了许多线程↓↓↓

image-20210524203407487


四、线程名字

1、默认线程名的源码分析

  • 有参构造器,指定线程名

image-20210524204011032


  • 无参构造器,默认为Thread- ,加上nextThreadNum,这个数字为0开始,自增

image-20210524204142202

image-20210524204110012


2、修改线程的名字

image-20210524204428170

image-20210524204508438

也就是说在线程没有启动之前,我们可以调用setName()方法去设置线程的名字,不然就是调用setNativeName(name),在C/C++层面去设置线程的默认名字,也就是我们上面的默认名字


五、守护线程

作用给用户线程提供服务

分类的标准看这个线程是否会阻止JVM的停止

特性

  • 线程类型默认继承自父线程
  • 被谁启动
  • 不影响JVM退出

守护线程和普通线程的区别

  • 整体区别
  • 唯一的区别在于是否影响JVM离开
    • 守护线程不会影响,JVM检测到用户线程全部执行完毕,就会关闭
    • 用户线程会影响,用户线程没执行完,JVM不会关闭

六、线程优先级

  • 10个优先级,默认为5

image-20210524205609688


  • 程序的设计不应该依赖于优先级
    • 不同的操作系统优先级不同
      • window中只有7个优先级
      • linux中所有线程优先级会被忽略,所有线程优先级一致
    • 优先级会被操作系统修改

总结

我们只需要了解线程有优先级的概念,优先级越大越优先即可,不需要去修改优先级

七、各属性总结

image-20210524210202078

八、面试常见问题

1、守护线程和普通线程的区别?

  • 整体上没有太大区别

  • 区别在于,是否影响JVM退出

  • 用户线程执行逻辑,守护线程是服务于用户线程

2、是否需要人为的设置守护线程?

我们不应该人为的将我们的线程设置为守护线程;

会出现数据不一致的问题

3、什么时候我们需要设置守护线程?

通常情况下JVM的守护线程足够我们使用,我们不需要设置守护线程

4、如果应用线程优先级来帮助程序运行?有哪些禁忌?

我们不应该通过程序优先级来帮助程序运行,线程优先级与操作系统息息相关,他会受到各个操作系统的影响,不容易实现控制,所以不推荐使用


②线程未异常处理

一、学习前的思考

  • Java异常体系图
  • 实际工作中,如何全局处理异常?为什么要全局处理?不处理行不行?

二、线程未捕获异常UncaughtException应该如何处理

1、为什么需要UncaughtExceptionHandler?

1)主线程可轻松的发现异常,子线程却不行

子线程发生异常,不影响主线程,不会终止主线程的程序,主线程将继续执行

/******
 @author 阿昌
 @create 2021-05-24 21:12
 *******
 * 单线程,抛出,处理异常堆栈
 * 多线程,子线程发生异常,会有什么不同???
 *      子线程发生异常,不影响主线程,不会终止主线程的程序,主线程将继续执行
 */
public class ExceptionInChildThread implements Runnable {
    @Override
    public void run() {
        throw new RuntimeException();
    }

    //主线程
    public static void main(String[] args) {
        new Thread(new ExceptionInChildThread()).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
        }
    }
}

2)子线程异常无法用传统异常捕获方式捕获

  • 不加try-catch 抛出4个异常,都带线程名
public class CantCatchDirectly implements Runnable {
    @Override
    public void run() {
        throw new RuntimeException();
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(new CantCatchDirectly(),"线程1").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(),"线程2").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(),"线程3").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(),"线程4").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(),"线程5").start();

    }
}

image-20210524212044115


  • 加了try-catch,期望捕获第一个线程的异常。线程234不应该运行,希望看到控制台打印异常信息Caught Exception
public class CantCatchDirectly implements Runnable {
    @Override
    public void run() {
        throw new RuntimeException();
    }

    public static void main(String[] args) throws InterruptedException {
        try {
            new Thread(new CantCatchDirectly(),"线程1").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(),"线程2").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(),"线程3").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(),"线程4").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(),"线程5").start();
        } catch (RuntimeException e) {
            System.out.println("抓住异常");
            e.printStackTrace();
        }

    }
}

发现异常根本抓不住!!!!!!!!!

image-20210524212525011

因为try-catch只能捕获到对应线程内的异常,他try-catch的是主线程的,而异常是发现在子线程中

image-20210524212837823


3)不能直接捕获的后果、提高健壮性


2、两种解决方案

1)方案一:手动在每个run方法里进行try-catch(不推荐)

public class CantCatchDirectly implements Runnable {
    @Override
    public void run() {
        try {
            throw new RuntimeException();
        } catch (RuntimeException e) {
            System.out.println("捕获异常");
        }
    }

    public static void main(String[] args) throws InterruptedException {
            new Thread(new CantCatchDirectly(),"线程1").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(),"线程2").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(),"线程3").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(),"线程4").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(),"线程5").start();
    }
}

image-20210524213235037


2)方案二:利用UncaughtExceptionHandler(推荐)

  • UncaughtExceptionHandler接口
  • void uncaughtException(Thread t,Throwable e);

image-20210524213518958


  • 异常处理器的调用策略

image-20210524213745026


  • 自定义UncaughtExceptionHanlder
/******
 @author 阿昌
 @create 2021-05-24 21:40
 *******
 *      自定义 UncaughtExceptionHandler
 */
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
    private String name;

    public MyUncaughtExceptionHandler(String name) {
        this.name = name;
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        Logger logger = Logger.getAnonymousLogger();
        logger.log(Level.WARNING,"线程异常:"+t.getName(),e);
        System.out.println(name+"我捕获了异常"+t.getName()+"异常名字:"+e);
    }
}
  • 使用自定义 UncaughtExceptionHandler
/******
 @author 阿昌
 @create 2021-05-24 21:44
 *******
 *  使用自定义的 UncaughtExceptionHandler
 */
public class UseMyUncaughtExceptionHandler implements Runnable {
    @Override
    public void run() {
            throw new RuntimeException();
    }

    public static void main(String[] args) throws InterruptedException {
        //设置自定义异常处理器
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("我们自定义的线程异常处理器"));

        new Thread(new UseMyUncaughtExceptionHandler(),"线程1").start();
        Thread.sleep(300);
        new Thread(new UseMyUncaughtExceptionHandler(),"线程2").start();
        Thread.sleep(300);
        new Thread(new UseMyUncaughtExceptionHandler(),"线程3").start();
        Thread.sleep(300);
        new Thread(new UseMyUncaughtExceptionHandler(),"线程4").start();
        Thread.sleep(300);
        new Thread(new UseMyUncaughtExceptionHandler(),"线程5").start();
    }
}

image-20210524214822915


三、线程的未捕获异常—常见面试题

1、Java异常体系是什么?

2、如何处理全局异常?为什么要全局异常处理?不处理行不行?

通过实现一个全局的UncaughtExceptionHandler接口

打印日志方便我们后期的维护,给前端统一的返回;

不处理不行

3、run方法是否可以抛出异常?如果抛出异常,线程的状态会怎么样?

不可以抛出异常,只能自己处理try-catch;

线程状态会获取到异常,并终止程序

4、线程中如何处理某个未处理异常?

通过实现一个全局的UncaughtExceptionHandler接口

以上是关于Day277.线程的各个属性多线程未捕获异常处理 -Juc的主要内容,如果未能解决你的问题,请参考以下文章

.net捕获全局异常并且记录日志多线程方式发送邮件提醒

commonruntimeexception错误怎么捕获

当其中一个线程中存在未捕获的异常时,Python 多线程程序不会退出

在 WPF 的子线程中捕获未处理的异常

Android视图:未捕获的处理程序:线程主因未捕获的异常而退出

面试官:线程池执行过程中遇到异常会发生什么,怎样处理?