3.1 线程组(ThreadGroup)
Java中用ThreadGroup来表示线程组,我们可以使用线程组对线程进行批量控制。
每个Thread必然存在于一个ThreadGroup中,Thread不能独立于ThreadGroup存在。执行main()方法线程的名字是main,如果zainew Thread 时没有显式指定,那么默认父线程(当前执行new Thread的线程) 线程组设置为自己的线程组。
/**
* @author :ls
* @date :Created in 2022/4/19 10:07
* @description:
*/
public class T1 {
public static void main(String[] args) {
Thread sub_thread = new Thread(()->{
System.out.println("sub_thread--->当前线程组名字- >"+Thread.currentThread().getThreadGroup().getName());
System.out.println("sub_thread--->当前线名字-->"+Thread.currentThread().getName());
});
sub_thread.start();
System.out.println("main方法----->"+Thread.currentThread().getName());
}
}
输出结果:
main方法----->main
sub_thread--->当前线程组名字-->main
sub_thread--->当前线名字-->Thread-0
ThreadGroup管理着它下面的Thread,ThreadGroup是一个标准的向下引用的树状结构,这样设计的原因是防止“上级”线程被“下级”线程引用而无法有效的被GC回收
3.2 线程的优先级
java中线程的优先级默认值是5,可指定范围为:1~10。在这里 java知识给操作系统一个优先级的参考值,线程最终在操作系统的优先级是多少还是由操作系统决定,也就是cpu对线程的调度,不完全是按优先级来执行的。
使用方法 setPriority() 来设定线程的优先级。
public class Demo {
public static void main(String[] args) {
Thread a = new Thread();
System.out.println("我是默认线程优先级:"+a.getPriority());
Thread b = new Thread();
b.setPriority(10);
System.out.println("我是设置过的线程优先级:"+b.getPriority());
}
}
输出结果:
我是默认线程优先级:5
我是设置过的线程优先级:10
当然这里设置线程的优先级,在实际的业务实现中是不建议采用的!! 首先来说 java线程中的优先级不是特可靠,其次硬件的配置(如 cpu) 不同对于线程执行来说也是有影响的。
public class Demo {
public static class T1 extends Thread {
@Override
public void run() {
super.run();
System.out.println(String.format("当前执⾏的线程是:%s,优先级:%d",
Thread.currentThread().getName(),
Thread.currentThread().getPriority()));
}
}
public static void main(String[] args) {
IntStream.range(1, 10).forEach(i -> {
Thread thread = new Thread(new T1());
thread.setPriority(i);
thread.start();
});
}
}
某次的输出:
当前执⾏的线程是:Thread-17,优先级:9
当前执⾏的线程是:Thread-1,优先级:1
当前执⾏的线程是:Thread-13,优先级:7
当前执⾏的线程是:Thread-11,优先级:6
当前执⾏的线程是:Thread-15,优先级:8
当前执⾏的线程是:Thread-7,优先级:4
当前执⾏的线程是:Thread-9,优先级:5
当前执⾏的线程是:Thread-3,优先级:2
当前执⾏的线程是:Thread-5,优先级:3
这个结果 多次执行,结果是不同的。
java 中 有个线程调度器来监视和控制处于RUNNABLE状态的线程。线程的调度策略采用抢占式,优先级高的线程比优先级低的线程有更大几率优先执行。 在优先级相同情况下,按照“先到先得”的原则。每个java程序都有一个默认的主线程,就是通过JVM启动的第一个main线程。
此外,还有一种线程称作:“守护线程 (Daemon)” 守护线程默认优先级比较低
- 如果某线程是守护线程,那如果所有的非守护线程结束,这个守护线程也会 自动结束。
- 应用场景是:当所有非守护线程结束时,结束其余的子线程(守护线程)自动关闭,就免去了还要继续关闭⼦线程的麻烦。
- 应用场景是:当所有非守护线程结束时,结束其余的子线程(守护线程)自动关闭,就免去了还要继续关闭⼦线程的麻烦。
线程与线程组的优先级不同时,将会怎样呢??
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("t1");
threadGroup.setMaxPriority(6);
Thread thread = new Thread(threadGroup,"thread");
thread.setPriority(9);
System.out.println("我是线程组的优先级"+threadGroup.getMaxPriority());
System.out.println("我是线程的优先级"+thread.getPriority());
}
输出:
我是线程组的优先级6
我是线程的优先级6
从结果来看,如果线程与所在线程组的优先级发生冲突时,该线程的优先级会失效,以线程组的最大优先级替代。
3.3 线程组的常用方法及数据结构
3.3.1 线程组的常用方法
获取当前线程组的名字:
Thread.currentThread().getThreadGroup().getName()
复制线程组:
Thread[] threads = new Thread[threadGroup.activeCount()];
TheadGroup threadGroup = new ThreadGroup();
threadGroup.enumerate(threads);
线程组 统一异常处理
/**
* @author :ls
* @date :Created in 2022/4/19 10:54
* @description:
*/
public class T2 {
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("group1"){
/**
* 继承ThreadGroup并重新定义一下方法
* 在线程成员抛出unchecked exception
* 会执行此方法
* */
@Override
public void uncaughtException(Thread t, Throwable e){
System.out.println(t.getName()+": "+e.getMessage());
}
};
Thread thread = new Thread(threadGroup,new Runnable() {
@Override
public void run() {
//抛出 unchecked 异常
throw new RuntimeException("测试异常!!");
}
},"mythread");
thread.start();
}
}
执行结果:
mythread: 测试异常!!
3.3.1 线程组的数据结构
线程组不仅可以包含线程,也可以包含线程。
ThreadGroup 中的成员变量:
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
private final ThreadGroup parent; // ⽗亲ThreadGroup
String name; // ThreadGroupr 的名称
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 方法,这里看看这个方法的源码:
// 检查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()方法,不过是用来当前运行的线程是 否有权限修改被调用的这个线程实例。(Determines if the currently running thread has permission to modify this thread.)
总结来说,线程组是一个树状的结构,每个线程组下面可以有多个线程或者线程 组。线程组可以起到统一控制线程的优先级和检查线程的权限的作用。