1 引言
在 Java 多线程编程中,线程组(ThreadGroup)和线程优先级是两个重要的概念。线程组用于管理一组相关的线程,而线程优先级则影响操作系统为线程分配处理器时间的顺序。本文将详细介绍线程组和线程优先级的使用方法及其背后的原理。
2 线程组(ThreadGroup)
Java 提供了 ThreadGroup 类来表示线程组,我们可以通过线程组对线程进行批量控制。每个 Thread 必然存在于一个 ThreadGroup 中,Thread 不能独立于 ThreadGroup 存在。执行 main() 方法的线程名字是 main,如果在 new Thread 时没有显式指定,那么默认将父线程(当前执行 new Thread 的线程)线程组设置为自己的线程组。
2.1 示例代码
Thread testThread = new Thread(() -> {System.out.println("testThread当前线程组名字:" +Thread.currentThread().getThreadGroup().getName());System.out.println("testThread线程名字:" +Thread.currentThread().getName());
});testThread.start();
System.out.println("执行main所在线程的线程组名字: " + Thread.currentThread().getThreadGroup().getName());
System.out.println("执行main方法线程名字:" + Thread.currentThread().getName());
输出结果:
执行main所在线程的线程组名字: main
testThread当前线程组名字:main
testThread线程名字:Thread-0
执行main方法线程名字:main
ThreadGroup 是一个标准的向下引用的树状结构,这样设计可以防止“上级”线程被“下级”线程引用而无法有效地被 GC 回收。
2.2 线程组的常用方法
-
获取当前线程的线程组名字
Thread.currentThread().getThreadGroup().getName() -
复制线程组
// 获取当前的线程组 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); // 复制一个线程组到一个线程数组(获取Thread信息) Thread[] threads = new Thread[threadGroup.activeCount()]; threadGroup.enumerate(threads); -
线程组统一异常处理
ThreadGroup group = new ThreadGroup("testGroup") {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(t.getName() + ": " + e.getMessage());} };Thread thread = new Thread(group, () -> {throw new RuntimeException("测试异常"); });thread.start();
2.3 线程组的数据结构
ThreadGroup 类中的成员变量:
public class ThreadGroup implements Thread.UncaughtExceptionHandler {private final ThreadGroup parent; // 父亲ThreadGroupString name; // ThreadGroup 的名称int maxPriority; // 最大优先级boolean destroyed; // 是否被销毁boolean daemon; // 是否守护线程boolean vmAllowSuspension; // 是否可以中断int nUnstartedThreads = 0; // 还未启动的线程int nthreads; // ThreadGroup中线程数目Thread threads[]; // ThreadGroup中的线程int ngroups; // 线程组数目ThreadGroup groups[]; // 线程组数组
}
构造方法:
// 私有构造方法
private ThreadGroup() {this.name = "system";this.maxPriority = Thread.MAX_PRIORITY;this.parent = null;
}// 默认是以当前ThreadGroup作为parent ThreadGroup,新线程组的父线程组是目前正在运行线程的线程组。
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 方法:
private static Void checkParentAccess(ThreadGroup parent) {parent.checkAccess();return null;
}public final void checkAccess() {SecurityManager security = System.getSecurityManager();if (security != null) {security.checkAccess(this);}
}
3 线程的优先级
线程优先级可以指定,范围是 1~10。但并不是所有的操作系统都支持 10 级优先级的划分(比如有些操作系统只支持 3 级划分:低、中、高),Java 只是给操作系统一个优先级的参考值,线程最终在操作系统中的优先级还是由操作系统决定。
Java 默认的线程优先级为 5,线程的执行顺序由调度程序来决定,线程的优先级会在线程被调用之前设定。通常情况下,高优先级的线程将会比低优先级的线程有更高的概率得到执行。Thread 类的 setPriority() 方法可以用来设定线程的优先级。
Thread a = new Thread();
System.out.println("我是默认线程优先级:" + a.getPriority());
Thread b = new Thread();
b.setPriority(10);
System.out.println("我是设置过的线程优先级:" + b.getPriority());
输出结果:
我是默认线程优先级:5
我是设置过的线程优先级:10
3.1 线程优先级的可靠性
Java 中的优先级不是特别的可靠,Java 程序中对线程所设置的优先级只是给操作系统一个建议,操作系统不一定会采纳。而真正的调用顺序,是由操作系统的线程调度算法来决定的。
static class MyThread extends Thread {@Overridepublic void run() {System.out.println("MyThread当前线程:" + Thread.currentThread().getName()+ ",优先级:" + Thread.currentThread().getPriority());}
}public static void main(String[] args) {for (int i = 1; i <= 10; i++) {Thread thread = new MyThread();thread.setName("线程" + i);thread.setPriority(i);thread.start();}
}
运行该程序,有时候可以按照优先级执行,有时却不行。
MyThread当前线程:线程3,优先级:3
MyThread当前线程:线程6,优先级:6
MyThread当前线程:线程5,优先级:5
MyThread当前线程:线程4,优先级:4
MyThread当前线程:线程2,优先级:2
MyThread当前线程:线程1,优先级:1
MyThread当前线程:线程8,优先级:8
MyThread当前线程:线程10,优先级:10
MyThread当前线程:线程7,优先级:7
MyThread当前线程:线程9,优先级:9
3.2 线程调度器
Java 提供了一个线程调度器来监视和控制处于 RUNNABLE 状态的线程。线程的调度策略采用抢占式的方式,优先级高的线程会比优先级低的线程有更大的几率优先执行。在优先级相同的情况下,会按照“先到先得”的原则执行。每个 Java 程序都有一个默认的主线程,就是通过 JVM 启动的第一个线程——main 线程。
3.3 守护线程(Daemon)
还有一种特殊的线程,叫做守护线程(Daemon),守护线程默认的优先级比较低。如果某线程是守护线程,那如果所有的非守护线程都结束了,这个守护线程也会自动结束。当所有的非守护线程结束时,守护线程会自动关闭,这就免去了还要继续关闭子线程的麻烦。线程默认是非守护线程,可以通过 Thread 类的 setDaemon 方法来设置为守护线程。
4 线程组和线程优先级之间的关系
如果某个线程的优先级大于线程所在线程组的最大优先级,那么该线程的优先级将会失效,取而代之的是线程组的最大优先级。
ThreadGroup group = new ThreadGroup("testGroup");
group.setMaxPriority(7);
Thread thread = new Thread(group, "test-thread");
thread.setPriority(10);
System.out.println("线程组的优先级是:" + group.getMaxPriority());
System.out.println("线程的优先级是:" + thread.getPriority());
输出:
线程组的优先级是:7
线程的优先级是:7
5 小结
Java 提供了 ThreadGroup 类来创建一组相关的线程,使线程组管理更方便;每个 Java 线程都有一个优先级,这个优先级会影响到操作系统为这个线程分配处理器时间的顺序。线程组和线程优先级的结合使用,可以帮助我们更好地管理和调度线程。
通过本文的学习,希望读者能够更好地掌握 Java 线程组和线程优先级的使用方法及其背后的原理,为后续深入学习 Java 并发编程打下坚实的基础。
6 思维导图

7 参考资料
线程组和线程优先级(冷门知识)
