文章

线程组和线程优先级

线程组和线程优先级

线程组


每个 Thread 必然存在于一个 ThreadGroup 中,Thread 不能独立于 ThreadGroup 存在。执行main()方法的线程名字是 main,如果在 new Thread 时没有显式指定,那么默认将父线程的线程组设置为自己的线程组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
    Thread thread = new Thread(() -> {
        // thread当前线程组名字:main
        System.out.println("thread当前线程组名字:" +
                Thread.currentThread().getThreadGroup().getName());
        // thread线程名字:Thread-0
        System.out.println("thread线程名字:" +
                Thread.currentThread().getName());
    });

    thread.start();
    // 执行main所在线程的线程组名字: main
    System.out.println("执行main所在线程的线程组名字: " + Thread.currentThread().getThreadGroup().getName());
    // 执行main方法线程名字:main
    System.out.println("执行main方法线程名字:" + Thread.currentThread().getName());
}

ThreadGroup 是一个标准的向下引用的树状结构,这样设计可以防止”上级”线程被”下级”线程引用而无法有效地被 GC 回收。每个线程组下面可以有多个线程或者线程组。线程组可以起到==统一控制线程的优先级和检查线程权限==的作用。

线程组的常用方法及数据结构

获取当前线程的线程组名字

1
Thread.currentThread().getThreadGroup().getName()

复制线程组

1
2
3
4
5
6
7
public static void main(String[] args) {
    // 获取当前的线程组
    ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
    // 复制一个线程组到一个线程数组(获取Thread信息)
    Thread[] threads = new Thread[threadGroup.activeCount()];
    threadGroup.enumerate(threads);
}

线程组统一异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {
    // 创建一个线程组,并重新定义异常
    ThreadGroup group = new ThreadGroup("testGroup") {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            // Thread-0: 测试异常
            System.out.println(t.getName() + ": " + e.getMessage());
        }
    };

    // 测试异常
    Thread thread = new Thread(group, () -> {
        // 抛出 unchecked 异常
        throw new RuntimeException("测试异常");
    });

    // 启动线程
    thread.start();
}

线程组的数据结构

成员变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
    // 父亲ThreadGroup
    private final ThreadGroup parent; 
    // ThreadGroup 的名称
    String name; 
    // 最大优先级
    int maxPriority; 
    // 是否被销毁
    boolean destroyed; 
    // 是否守护线程
    boolean daemon; 
    // 是否可以中断
    boolean vmAllowSuspension; 
    // 还未启动的线程
    int nUnstartedThreads = 0; 
    // ThreadGroup中线程数目
    int nthreads; 
    // ThreadGroup中的线程
    Thread threads[]; 
	// 线程组数目
    int ngroups; 
    // 线程组数组
    ThreadGroup groups[]; 
}

构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 私有构造方法
private ThreadGroup() {
    this.name = "system";
    this.maxPriority = Thread.MAX_PRIORITY;
    this.parent = null;
}

// 默认是以当前的线程组作为父线程组
public ThreadGroup(String name) {
    this(Thread.currentThread().getThreadGroup(), name);
}

// 构造方法
public ThreadGroup(ThreadGroup parent, String name) {
    this(checkParentAccess(parent), parent, name);
}

// 私有构造方法,主要的构造函数
private ThreadGroup(Void unused, ThreadGroup parent, String name) {
    this.name = name;
    this.maxPriority = parent.maxPriority;
    this.daemon = parent.daemon;
    this.vmAllowSuspension = parent.vmAllowSuspension;
    this.parent = parent;
    parent.add(this);
}

checkParentAccess() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 检查parent ThreadGroup
private static Void checkParentAccess(ThreadGroup parent) {
    parent.checkAccess();
    return null;
}

// 判断当前运行的线程是否具有修改线程组的权限
public final void checkAccess() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkAccess(this);
    }
}

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

Thread 类也有一个 checkAccess 方法,不过是用来判断当前运行的线程是否有权限修改被调用的这个线程。

线程优先级


线程优先级可以指定,范围是 1~10(由低到高,默认是5)。Java 中的优先级不是特别的可靠,Java 程序中对线程所设置的优先级只是给操作系统一个建议,==操作系统不一定会采纳==。而真正的调用顺序,是由操作系统的线程调度算法来决定的。Thread类的setPriority()方法可以用来设定线程的优先级。

Java 提供了一个线程调度器来监视和控制处于RUNNABLE 状态的线程。

  • 线程的调度策略采用抢占式的方式,优先级高的线程会比优先级低的线程有更大的几率优先执行。
  • 在优先级相同的情况下,会按照“先到先得”的原则执行。
  • 每个 Java 程序都有一个默认的主线程,就是通过 JVM 启动的第一个线程——main 线程。

还有一种特殊的线程,叫做守护线程(Daemon),守护线程默认的优先级比较低。

  • 如果某线程是守护线程,那如果所有的非守护线程都结束了,这个守护线程也会自动结束。
  • 当所有的非守护线程结束时,守护线程会自动关闭,这就免去了还要继续关闭子线程的麻烦。
  • 线程默认是非守护线程,可以通过 Thread 类的 setDaemon 方法来设置为守护线程。

线程组和线程优先级之间的关系


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
    // 创建一个线程组
    ThreadGroup group = new ThreadGroup("testGroup");
    // 将线程组的优先级指定为 7
    group.setMaxPriority(7);

    // 创建一个线程,将该线程加入到 group 中
    Thread thread = new Thread(group, "test-thread");
    // 企图将线程的优先级设定为 10
    thread.setPriority(10);
    
    // 输出线程组的优先级和线程的优先级,结果都是7
    System.out.println("线程组的优先级是:" + group.getMaxPriority());
    System.out.println("线程的优先级是:" + thread.getPriority());
}

如果某个线程的优先级大于线程所在线程组的最大优先级,那么该线程的优先级将会失效,取而代之的是线程组的最大优先级。

本文由作者按照 CC BY 4.0 进行授权