zoukankan      html  css  js  c++  java
  • 并发和多线程(五)--线程相关属性和线程异常处理

    1、线程id和name

      线程id是线程的唯一标识,不可修改,而线程名称是可以修改的。

    public static void main(String[] args) {
        Thread thread = new Thread();
        System.out.println("主线程ID为:"+Thread.currentThread().getId());
        System.out.println("主线程ID为:"+thread.getId());
        System.out.println("主线程name为:"+thread.getName());
        thread.setName("thread58");
        System.out.println("主线程name为:"+thread.getName());
    }
    结果:
    主线程ID为:1
    主线程ID为:12
    主线程name为:Thread-0
    主线程name为:thread58

      从结果看到,主线程的id为1,所以线程的id也是从1开始的,而新建的子线程的id为12,而不是我们猜想的2。

      通过查看源码,知道线程id的规则如下:

    public long getId() {
        return tid;
    }
    
    //通过调用Thread构造器初始化Thread,而tid = nextThreadID()
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    
        
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

      这里是++threadSeqNumber实现自增,那为什么子线程的id不是2呢,是因为Jvm在运行代码的时候还会启动别的线程帮助程序运行和处理,例如垃圾收集器等,通过debug我们就可以看到。

       而线程的name值,只是对线程的命名,除了默认情况下的命名,我们还可以通过setName()修改,而且可以多个线程name相同。

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

      通过源码看到,默认没有name的情况下,通过"Thread-" + nextThreadNum()进行命名,而这个方法是threadInitNumber++,所以从Thread-0开始。

     2、守护线程

       对于jvm来说,一般我们创建的线程都是用户线程,除了用户线程就是守护线程,守护线程为了程序运行而服务,守护用户线程。一般来说,守护线程被jvm启动,而用户线程被主线程启动。根本的区别就是,如果jvm发现没有当前没有用户线程在运行,jvm就会退出,守护线程对jvm退出没有影响。

      线程类型默认继承父线程,例如,在main方法中创建一个线程,主线程就是用户线程,所以被创建的线程默认也是用户线程。同样的,守护线程创建的线程也是守护线程。

    //判断当前线程是否Wie守护线程
    public final boolean isDaemon() {
        return daemon;
    }
    //设置当前线程为守护线程。true,守护线程,false,用户线程
    public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }

    PS:

      开发过程中,不要将线程设置为守护线程,一旦设置为守护线程,jvm发现没有用户线程在运行,就直接关闭了,就会导致我们的程序没有执行完就关闭了。

    3、线程优先级

      线程优先级是指线程启动执行的优先级,对于Java来说,一共10个优先级,默认为5。准确的说,线程优先级也是继承父类优先级,如果你把主函数设置为8,新建的子线程默认也是8.

    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;
    
    /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;
    
    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
    
    //设置线程优先级
    public final void setPriority(int newPriority) {
        
    }
    
    //获取线程优先级
    public final int getPriority() {
        return priority;
    }

       开发中,我们不应该依赖线程优先级,因为我们在使用多线程的时候,发现优先级高的线程不一定比优先级低的线程先执行,只能说概率更高而已。而且,1-10的优先级只是jvm的划分,总归要和OS挂钩的,不同的OS对优先级的划分不同,需要进行映射。例如Windows有7个优先级,1和2对应1,3和4对应2。。。而Linux系统下Java线程优先级会被忽略,不能起作用。在solaris系统中,又是不同的。

      设置线程优先级,还有可能带来的问题,就是某些优先级低的线程可能一直无法获得CPU使用权,也就是一直保持"饥饿"状态。所以,综上,完全不建议修改线程优先级。

    4、如何捕获线程异常?

    public static void main(String[] args) {
        Thread thread = new Thread(new ThreadClass());
        thread.start();
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
    
    @Override
    public void run() {
        throw new RuntimeException();
    }

      通过上面的代码的运行,可以看到,子线程发生异常,主线程还是继续运行,如果运行在服务器上面,可能都不知道程序有出现过异常。所以,我们需要对Thread的代码进行异常处理,例如try catch。

    try catch处理线程异常:

    try {
        Thread thread = new Thread(() -> {
            throw new RuntimeException();
        });
        Thread thread1 = new Thread(() -> {
            throw new RuntimeException();
        });
        Thread thread2 = new Thread(() -> {
            throw new RuntimeException();
        });
        Thread thread3 = new Thread(() -> {
            throw new RuntimeException();
        });
        thread.start();
        thread1.start();
        thread2.start();
        thread3.start();
    } catch (RuntimeException e) {
        System.out.println("当前线程发生异常");
    }
    结果:
    Exception in thread "Thread-0" Exception in thread "Thread-3" Exception in thread "Thread-2" Exception in thread "Thread-1" java.lang.RuntimeException
        at com.diamondshine.Thread.MyRunnableClass.lambda$main$0(MyRunnableClass.java:18)
        at java.lang.Thread.run(Thread.java:745)
    java.lang.RuntimeException
        at com.diamondshine.Thread.MyRunnableClass.lambda$main$2(MyRunnableClass.java:24)
        at java.lang.Thread.run(Thread.java:745)
    java.lang.RuntimeException
        at com.diamondshine.Thread.MyRunnableClass.lambda$main$3(MyRunnableClass.java:27)
        at java.lang.Thread.run(Thread.java:745)
    java.lang.RuntimeException
        at com.diamondshine.Thread.MyRunnableClass.lambda$main$1(MyRunnableClass.java:21)
        at java.lang.Thread.run(Thread.java:745)
    View Code

      四个线程都发生异常,我们通过try catch去处理,发现子线程发生异常,并没有被catch捕获。原因是try catch可以捕获主线程的异常,却不能捕获子线程的异常,导致即使有了try catch也是不行的。正确的方式:

    UncaughtExceptionHandler

    //自定义UncaughtExceptionHandler
    
    @Slf4j
    public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
    
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            log.error("当前线程:{}发生异常,异常信息为:{}", t.getName(), e.getMessage());
            //后续报警等相关逻辑
        }
    }
    public static void main(String[] args) {
    
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
    
            Thread thread = new Thread(() -> {
                throw new RuntimeException();
            });
            Thread thread1 = new Thread(() -> {
                throw new RuntimeException();
            });
            Thread thread2 = new Thread(() -> {
                throw new RuntimeException();
            });
            Thread thread3 = new Thread(() -> {
                throw new RuntimeException();
            });
            thread.start();
            thread1.start();
            thread2.start();
            thread3.start();
        
    }
    结果:
    16:02:42.288 [Thread-3] ERROR com.diamondshine.Thread.MyUncaughtExceptionHandler - 当前线程:Thread-3发生异常,异常信息为:
    java.lang.RuntimeException: null
        at com.diamondshine.Thread.MyRunnableClass.lambda$main$3(MyRunnableClass.java:26)
        at java.lang.Thread.run(Thread.java:745)
    16:02:42.288 [Thread-0] ERROR com.diamondshine.Thread.MyUncaughtExceptionHandler - 当前线程:Thread-0发生异常,异常信息为:
    java.lang.RuntimeException: null
        at com.diamondshine.Thread.MyRunnableClass.lambda$main$0(MyRunnableClass.java:17)
        at java.lang.Thread.run(Thread.java:745)
    16:02:42.288 [Thread-1] ERROR com.diamondshine.Thread.MyUncaughtExceptionHandler - 当前线程:Thread-1发生异常,异常信息为:
    java.lang.RuntimeException: null
        at com.diamondshine.Thread.MyRunnableClass.lambda$main$1(MyRunnableClass.java:20)
        at java.lang.Thread.run(Thread.java:745)
    16:02:42.288 [Thread-2] ERROR com.diamondshine.Thread.MyUncaughtExceptionHandler - 当前线程:Thread-2发生异常,异常信息为:
    java.lang.RuntimeException: null
        at com.diamondshine.Thread.MyRunnableClass.lambda$main$2(MyRunnableClass.java:23)
        at java.lang.Thread.run(Thread.java:745)

      从结果看,我们自定义的异常处理器有效,成功的捕获到异常,这时候不仅仅打印日志,可以根据自己的使用场景进行逻辑处理,例如:让监控系统报警等。

  • 相关阅读:
    Codeforces 893E Counting Arrays:dp + 线性筛 + 分解质因数 + 组合数结论
    Codeforces 938E Max History:排列 + 逆元【考虑单个元素的贡献】
    Codeforces 859E Desk Disorder:并查集【两个属性二选一】
    Codeforces 869C The Intriguing Obsession:组合数 or dp
    Codeforces 888D Almost Identity Permutations:错排公式
    Codeforces 870E Points, Lines and Ready-made Titles:并查集【两个属性二选一】
    Codeforces 895C Square Subsets:状压dp【组合数结论】
    leetcode
    hdu6578 2019湖南省赛D题Modulo Nine 经典dp
    Codechef March Cook-Off 2018. Maximum Tree Path
  • 原文地址:https://www.cnblogs.com/huigelaile/p/11749209.html
Copyright © 2011-2022 走看看