第三章 线程组和线程优先级
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方法----->mainsub_thread--->当前线程组名字-->mainsub_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 ThreadGroupprivate 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.)
总结来说,线程组是一个树状的结构,每个线程组下面可以有多个线程或者线程 组。线程组可以起到统一控制线程的优先级和检查线程的权限的作用。