Interviewer: Have you learned about thread groups and thread priorities?

foreword

A long-term series of tutorials is currently being Java多线程专题published, from introductory to advanced 源码解读, there will be more space, if you like it, give it a follow ❤️ ~

What is a thread group

In Java, thread groups use ThreadGrouprepresentations, which are Threadstored in thread groups, and the literal meaning is also well understood. In the process of creating a thread, it Threadcannot be independent of the thread group. When we learned to create a thread before, we did not specify a thread group, because by default, it will use the current thread environment as a thread group, which can be obtained by Thread.currentThread().getThreadGroup()obtaining the thread group, thread Groups can easily manage our threads, which improves security to a certain extent

public class ThreadGroupTest {
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getThreadGroup().getName());
        }).start();
        System.out.println(Thread.currentThread().getThreadGroup().getName());
    }
}
复制代码

output:

main
main
复制代码

It can be found under the mainthread group

ThreadGroupIs a standard downward reference tree structure, the reason for this design is to prevent "upper" threads from being referenced by "lower" threads and cannot be effectively recycled by GC .

thread priority

The level of threads is 优先级determined by the operating system. Different operating systems have different levels. In Java, a level range is provided 1~10for our reference. The default thread priority of Java is 5that the execution 调度程序order of the thread is determined by the thread, and the priority of the thread will be 调用之前set in the thread.

Source code description:

  /**
     * 最低级别
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * 默认级别
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * 最高级别
     */
    public final static int MAX_PRIORITY = 10;
复制代码

Get thread priority

 public static void main(String[] args) {
    new Thread(() -> {
        System.out.println("default level: {}" + Thread.currentThread().getPriority());
    }).start();
}
复制代码

output:default level: {}5

set level

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        System.out.println("default level: {}" + Thread.currentThread().getPriority());
    });
    t.start();
    t.setPriority(10);
}
复制代码

output:default level: {}10

Generally speaking, high-level priorities tend to be 几率executed higher. Note that this is a probabilistic problem. Let's test it.

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        System.out.println("hello " +  Thread.currentThread().getPriority());
        System.out.println("default level: {}" + Thread.currentThread().getPriority());
    });
    t.setPriority(3);

    Thread t1 = new Thread(() -> {
        System.out.println("hello " +  Thread.currentThread().getPriority());
        System.out.println("default level: {}" + Thread.currentThread().getPriority());
    });
    t1.setPriority(7);

    Thread t2 = new Thread(() -> {
        System.out.println("hello " +  Thread.currentThread().getPriority());
        System.out.println("default level: {}" + Thread.currentThread().getPriority());
    });
    t2.setPriority(10);


    t.start();
    t1.start();
    t2.start();
}

复制代码

First output:

hello 7
default level: {}7
hello 10
default level: {}10
hello 3
default level: {}3

复制代码

the second time:

hello 3
default level: {}3
hello 7
default level: {}7
hello 10
default level: {}10
复制代码

the third time

hello 10
default level: {}10
hello 7
default level: {}7
hello 3
default level: {}3

复制代码

...

It is found that after continuous attempts, the probability of high-level appearance will be higher, so students who want to use it to complete some specific businesses should pay attention to it, it is not recommended to use it . As 不靠谱mentioned before, the bottom layer is still scheduled by the operating system.

Java提供一个线程调度器来监视和控制处于RUNNABLE状态的线程。线程的调度策略采用抢占式,优先级高的线程比优先级低的线程会有更大的几率优先执行。在优先级相同的情况下,按照“先到先得”的原则。每个Java程序都有一个默认的主线程,就是通过JVM启动的第一个线程main线程。

除了主线程之外,还有一个线程是守护线程,它的优先级比较低。如果所有的非守护线程都结束了,这个守护线程也会自动结束。可以借助它实现一些特定场景,比如手动关闭线程的场景,某些场景下不关闭,会造成资源浪费,手动关闭又很麻烦。这里我们可以通过setDaemon(true)指定

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        System.out.println("hello " +  Thread.currentThread().getPriority());
        System.out.println("default level: {}" + Thread.currentThread().getPriority());
    });
    t.setDaemon(true); // 默认为false
    t.setPriority(10);

    Thread t1 = new Thread(() -> {
        System.out.println("hello " +  Thread.currentThread().getPriority());
        System.out.println("default level: {}" + Thread.currentThread().getPriority());
    });
    t1.setPriority(7);

    t.start();
    t1.start();
}
复制代码

输出:

hello 7
default level: {}7
hello 10
default level: {}10
复制代码

发现即使指定了高级别,执行的优先级仍然是最低的

线程组下的优先级

刚刚我们都是在main线程组下,举一反三,线程组下的优先级又是怎么样的呢❓下面,测试一下

public static void main(String[] args) {
        // 指定 name 为 g1的线程组
        ThreadGroup group = new ThreadGroup("g1");
        group.setMaxPriority(4);

        Thread t = new Thread(group, () -> {
            System.out.println("hello " +  Thread.currentThread().getPriority());
            System.out.println("default level: {}" + Thread.currentThread().getPriority());
        }, "t0");
        t.setPriority(10);
        t.start();
    }
复制代码

输出:

hello 4
default level: {}4
复制代码

发现,在g1线程组下指定了最大优先级后,线程t0的优先级最大级别只能是4, 所以这也是使用线程组的好处。

我们可以通过如下方式复制线程组, ThreadGroup提供了enumerate方法

public static void main(String[] args) throws InterruptedException {
        // 指定 name 为 g1的线程组
        ThreadGroup group = new ThreadGroup("g1");
        group.setMaxPriority(4);

        Thread t = new Thread(group, () -> {
            System.out.println("hello " +  Thread.currentThread().getPriority());
            System.out.println("default level: {}" + Thread.currentThread().getPriority());
        }, "t0");
        t.setPriority(10);
        t.start();

        // 复制线程组
        System.out.println(group.activeCount()); // 1
        Thread[] list = new Thread[group.activeCount()];
        group.enumerate(list);

        Thread.sleep(3000);
        System.out.println(list[0].getName()); // 输出 t0

    }

复制代码

统一异常捕获

    public static void main(String[] args) {
        // 指定 name 为 g1的线程组
        ThreadGroup group = new ThreadGroup("g1") {
            // 统一异常捕获
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println(t.getName() + ": " + e.getMessage()); // t0: 我出错了
            }
        };
        group.setMaxPriority(4);

        Thread t = new Thread(group, () -> {
            System.out.println("hello " +  Thread.currentThread().getPriority());
            System.out.println("default level: {}" + Thread.currentThread().getPriority());
            throw new RuntimeException("我出错了");
        }, "t0");
        t.setPriority(10);
        t.start();
    }
复制代码

向下引用的树状数据结构

线程组的内部其实不单单可以放线程,其实也可以放其它线程组,我们看下源码定义

    private final ThreadGroup parent;
    String name;
    int maxPriority;
    boolean destroyed;
    boolean daemon;
    boolean vmAllowSuspension;

    int nUnstartedThreads = 0;
    int nthreads;
    Thread threads[];

    int ngroups;
    ThreadGroup groups[];
复制代码

这里大家可以大胆去猜测一下,为什么要采用这种数据结构❓其实你通过源码发现,它的内部很多地方都调用了checkAccess方法,特别是在set操作,字面意思是检查是否有权限,我看下这个方法

public final void checkAccess() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkAccess(this);
    }
}
复制代码

它调用了一个SecurityManager, 它是Java的安全管理器,它允许应用程序在执行一个可能不安全或敏感的操作前确定该操作是什么,以及是否是在允许执行该操作的安全上下文中执行它。应用程序可以允许或不允许该操作。总的来说就是保证安全性。

通过上面的了解,我们应该知道为什么要用这种树状结构了。它都是一层一层级别的控制,这么做方便去管理,提高安全性,出了问题也能很快的定位到。就像公司的人员组织架构一样,一切都是为了管理好公司。

结束语

本篇内容到这里就结束了, 大家自己一定要多去理解,不要去背, 下期给大家讲讲线程的状态以及它的生命周期~

往期内容推荐

项目源码(源码已更新 欢迎star⭐️)

推荐 SpringBoot & SpringCloud (源码已更新 欢迎star⭐️)

Guess you like

Origin juejin.im/post/7104208101670125576