• 并发编程之线程属性


      本文主要讨论线程的各种属性,包括:主线程、线程优先级、守护线程、线程组、处理未捕获异常的处理器。

     
    主线程
     
      任何一个Java程序启动时,至少会启动一个线程执行main方法,这个线程就被称为主线程(Main Thread)。它是产生其它线程的线程,即:其它所有线程的主线程的子孙线程。通常,主线程都是最后结束的,因为它要执行其它子线程的关闭工作。
     
    线程优先级
     
      计算机只有一个CPU,各个线程轮流获取CPU的使用权,才能执行任务。为了判断优先执行哪个线程,给每一个线程设置了一个优先级,优先级高的线程将会被优先执行。默认情况下,每个线程的优先级继承它的父线程(创建该线程的线程)的优先级。优先级是1-10之间的整数,一般情况下,线程默认的优先级是5。常用的优先级有三个(1 : MIN_PRIORITY、 5 : NORM_PRIORITY、 10 : MAX_PRIORITY)。
    public class Priority {
    
    
        @Test
        public void defaultPriority(){
            int mainThreadPriority = Thread.currentThread().getPriority();
            System.out.println("default priority is "+mainThreadPriority);//5
        }
    
    
        @Test
        public void extendFather(){
            Thread mainThread = Thread.currentThread();
            mainThread.setPriority(4);
            int mainThreadPriority = mainThread.getPriority();
            System.out.println("main thread's priority is "+mainThreadPriority);//4
            Thread t1 = new Thread(() -> System.out.println(Thread.currentThread().getName()),"t1");
            System.out.println("t1 thread's priority is "+t1.getPriority());//4
        }
    }

    相关API:

    void setPriority(int priority)    设置线程优先级
    int getPriority()             获取线程优先级

    备注:

      1. 不要让业务依赖于线程优先级。(优先级只能让线程获取更多的机会优先执行,但不一是一定会优先执行)

      2. 一般情况下,不会对线程设定优先级

    守护线程

        守护线程(Daemon Thread)的唯一作用就是为其它线程提供服务,永远不要在守护线程中访问固有资源,比如文件、数据库等。
     
     守护线程能够结束自己的生命周期,即:当所有的用户线程都结束后,守护线程就会结束。且当只剩下守护线程,虚拟机就会退出。
     
     守护线程一般用来处理一些后台工作(比如JDK的垃圾回收线程),因此有时也被称为后台线程。
     
        默认情况下,由用户线程创建的线程仍是用户线程,由守护线程创建的线程仍是守护线程。
     
        Java虚拟机的垃圾回收线程就是典型的守护线程。
     
        相关API:
    void setDaemon(boolean isdaemon) 设置线程为守护线程或者用户线程。该方法必须在线程启动之前调用。
    boolean isDaemon()          判断该线程是否为后台线程

    线程组

        线程组(Thread Group)是一个可以统一管理的线程集合,线程组也可以包含其它线程组。默认情况下,所有的线程属于同一个线程组。线程只能访问自己所在线程组的信息,不能访问其它线程组的信息,包括该线程组的父线程组。
     
        建议不要使用线程组(已经有更好的特性用于线程集合的操作)
    public class ThreadGroupDemo {
    
        public static void print(){
            Thread thread = Thread.currentThread();
            System.out.println(thread.getThreadGroup().getName()+"-"+thread.getName());
        }
        
        public static void main(String[] args) {
            ThreadGroup group = new ThreadGroup("Print Group");
            new Thread(group, ThreadGroupDemo::print, "t1").start();
            new Thread(group, ThreadGroupDemo::print, "t2").start();
            group.list();
    
        }
    }

     补充:

    • 线程组并不能提供对线程的管理,主要是用来组织线程
    • 线程组中的线程优先级不能大于该线程组的优先级
    • interrupt一个线程组,会导致线程组中的所有active线程都被interrupt
    • 如果线程组被设置为daemon,当线程组中没有active线程,线程组将自动销毁

    未捕获异常处理器

      线程的run()不能抛出任何被检测的异常【因为Runnable接口中定义的run()没有抛出异常,所以重写run()时,不允许抛出异常,可以使用try-catch捕获异常】。但是,如果不被检测的异常没有使用try-catch处理,发生异常时会导致线程死亡。(比如下面这个例子,控制台并没有输出“Endind?”)

    public class UncaughtExceptionDemo implements Runnable{
    
        @Override
        public void run() {
            int i = 1/0;
            System.out.println("Endind?");
        }
    
    
        public static void main(String[] args) {
            ExecutorService service = Executors.newFixedThreadPool(1);
            service.execute(new UncaughtExceptionDemo());
        }
    }
    
    /* log:
    ...
    Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
        at concurrency.attributes.UncaughtExceptionDemo.run(UncaughtExceptionDemo.java:10)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
    */

      而且,在run中手动抛出了一个运行时异常,在main中使用try-catch处理异常,并未生效。(如下例,控制台并未输出"Current thread occurs exception.")

    public class UncaughtExceptionDemo implements Runnable{
    
        @Override
        public void run() {
            int i = 1/0;
        }
    
    
        public static void main(String[] args) {
            try{
                ExecutorService service = Executors.newFixedThreadPool(1);
                service.execute(new UncaughtExceptionDemo());
            } catch (RuntimeException e){
                System.out.println("Current thread occurs exception.");
            }
        }
    }
    
    /*
    ...
    Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
        at concurrency.attributes.UncaughtExceptionDemo.run(UncaughtExceptionDemo.java:10)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
    */

       那么,应该如何处理线程中抛出的异常呢?事实上,异常发生后,在线程死亡之前,异常会被传递到一个用于未捕获异常的处理器。可以使用该处理器来处理线程中抛出的异常。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    
    public class ThreadExceptionResolve implements Runnable{
    
    
        @Override
        public void run() {
            int i = 1/0;
            System.out.println("Endind?");
        }
    
    
        public static void main(String[] args) {
            //2. 为所有的线程设置“异常处理器”
            Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
            //3. 创建并执行线程
            ExecutorService service = Executors.newFixedThreadPool(1);
            service.execute(new ThreadExceptionResolve());
        }
    }
    
    
    //1. 定义符合线程异常处理器规范的“异常处理器”
    // 该处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类
    class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    
    
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("The acculation is error.");
        }
    }
    
    /*
    ...
    The acculation is error.
    */

      安装处理器的方式有两种:可以使用Thread的静态方法setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)为所有线程设置默认处理器,也可以使用Thread的实例方法setUncaughtExceptionHandler(UncaughtExceptionHandler eh)为摸一个线程设定处理器。如果线程没有安装处理器,此时的处理器就是该线程的ThreadGroup对象。

    //为所有线程设置默认处理器
    Thread.setDefaultUncaughtExceptionHandler(handler);
    
    
    //为指定线程设置处理器
    Thread mainThread = Thread.currentThread();
    mainThread.setUncaughtExceptionHandler(handler);

      

  • 相关阅读:
    BZOJ 2119: 股市的预测(后缀数组+rmq)
    2018.12.26 考试(哈希,二分,状压dp)
    ural 1297. Palindrome(后缀数组+rmq)
    URAL 1996. Cipher Message 3(KMP+fft)
    2019.4.11 一题 XSY 1551 ——广义后缀数组(trie上后缀数组)
    bzoj 2553 [BeiJing2011]禁忌——AC自动机+概率DP+矩阵
    洛谷 5291 [十二省联考2019]希望(52分)——思路+树形DP
    LOJ 2587 「APIO2018」铁人两项——圆方树
    洛谷 5289 [十二省联考2019]皮配——分开决策的动态规划
    洛谷 5284 [十二省联考2019]字符串问题——后缀数组+线段树优化连边+真实字典序排序思路
  • 原文地址:https://www.cnblogs.com/BlueStarWei/p/11622421.html
Copyright © 2020-2023  润新知