一、线程组(ThreadGroup)
Java中使用ThreadGroup来表示线程组,通过线程组来批量的管理线程。
每个Thread不能独立于ThreadGroup存在,必须存在于某个ThreadGroup中,如果
new Thread()时没有指定ThreadGroup,那么 默认将父线程(当前负责执行new Thread操作的线程)作为ThreadGroup。
示例代码如下:
public class ThreadGroupStu {
public static void main(String[] args) {
new Thread(()->{
System.out.println("thread1执行了");
System.out.println("thread1的线程组为:"+Thread.currentThread().getThreadGroup().getName());
System.out.println("thread1当前线程为:"+Thread.currentThread().getName());
}).start();
System.out.println();
System.out.println("执行main当前线程为:"+Thread.currentThread().getName());
}
}
运行结果如下:
执行main当前线程为:main
thread1执行了
thread1的线程组为:main
thread1当前线程为:Thread-0
二、线程优先级
Java中可以为线程指定优先级,范围是1~10。但并不是所有的操作系统都支持10级的优先级划分。Java只是给操作系统提供一个优先级的参考,具体的线程在操作系统执行先后的顺序还是由操作系统决定。
Java默认的线程优先级是5,线程的优先级在线程调度之前设定,线程的执行顺序由调度程序决定。
通常情况下,高优先级的线程是要比低优先级的线程有更高的几率优先执行,注意是更高的几率,也就是说不一定会 优先执行。
设置线程的优先级通过Thread类的setPriority()方法设置。
代码示例如下:
public class ThreadGroupStu {
@Test
public void testOne(){
Thread thread1= new Thread();
System.out.println("thread1的优先级为:"+thread1.getPriority());
Thread thread2 = new Thread();
thread2.setPriority(10);
System.out.println("thread2的优先级为:"+thread2.getPriority());
}
}
输出内容:
thread1的优先级为:5
thread2的优先级为:10
既然有了1-10的优先级,那么我们是不是可以在业务实现时通过这种方法去设置线程的优先级呢? 不可以!!!!
Java线程中的优先级并不可靠,他只是给操作系统一个优先级的建议,真正具体怎么个顺序执行,还是由线程的调度算法决定的。
代码示例:
public class ThreadGroupStu {
@Test
public void testTwo(){
IntStream.range(1,10).forEach(i->{
Thread thread = new Thread(() -> {
System.out.println(String.format("当前执行的线程是:%s , 优先级为: %d",Thread.currentThread().getName(),
Thread.currentThread().getPriority()));
});
thread.setPriority(i);
thread.start();
});
}
}
某次的执行结果如下:
当前执行的线程是:Thread-4 , 优先级为: 5
当前执行的线程是:Thread-3 , 优先级为: 4
当前执行的线程是:Thread-6 , 优先级为: 7
当前执行的线程是:Thread-7 , 优先级为: 8
当前执行的线程是:Thread-8 , 优先级为: 9
当前执行的线程是:Thread-5 , 优先级为: 6
当前执行的线程是:Thread-2 , 优先级为: 3
由此可见,在操作系统中,线程优先级别高的并不一定会优先执行。
在之前有说到,一个线程必须 存在ThreadGroup中,而ThreadGroup也有优先级。当线程优先级和所属的ThreadGroup的优先级不一致时,会有两种情况:
- 当线程设置的优先级大于线程组的优先级时,会强制使用线程组的优先级。
- 当线程设置的优先级小于等于线程组的优先级时,则就是用设置的优先级值。
源码如下:
Thread类的setPriority()
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
代码示例如下:
public class ThreadGroupStu {
@Test
public void testThree(){
ThreadGroup group = new ThreadGroup("group1");
group.setMaxPriority(6);//设置线程组的优先级为6
Thread thread = new Thread(group,"thread-1");
thread.setPriority(10);
Thread thread1 = new Thread(group,"thread-2");
thread1.setPriority(5);
System.out.println("group1的优先级为:"+group.getMaxPriority());
System.out.println("thread_1的优先级为:"+thread.getPriority());
System.out.println("thread_2的优先级为:"+thread.getPriority());
}
}
执行结果如下:
group1的最大优先级为:6
thread_1的优先级为:6
thread_2的优先级为:6
知识点:
Java提供了一个线程调度器,用来监视处于RUNNABLE状态的线程。线程的调度策略采用的时抢占式,优先级高的比优先级低的线程有更高的几率优先执行。在优先级相同的情况下,操作系统依照 “先到先得” 的原则执行线程。每个Java程序都有一个默认的主线程,就是通过JVM启动的第一个线程的main线程。
Java里面线程分为 非守护线程 和 守护线程(Daemon),守护线程的优先级比较低。
在操作系统中,如果所有的非守护线程都结束,那么守护线程也会自动结束。
一个线程默认是非守护线程。可以通过Thread类的setDaemon(boolean on)来设置。
三、线程组ThreadGroup的常用方法及源码分析
一、ThreadGroup的常用方法
1.获取当前线程的名字
Thread.currentThread().getThreadGroup().getName();
2.线程组统一处理异常
@Test
public void testFive(){
//创建线程组
ThreadGroup group = new ThreadGroup("group"){
//重新定义以下方法,在线程成员抛出unchecked exception,会执行此方法
public void uncaughtException(Thread t,Throwable e){
System.out.println(t.getName()+":"+e.getMessage());
}
};
//创建线程并绑定线程组,手动抛出异常
new Thread(group, () -> {
throw new RuntimeException("测试ThreadGroup统一处理异常");
}).start();
}
二、线程组源码分析
1.线程组的成员变量
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
private final ThreadGroup parent; //父线程组
String name; //线程组名称
int maxPriority; //优先级
boolean destroyed; //是否销毁
boolean daemon; //是否守护线程
boolean vmAllowSuspension; //是否可中断
int nUnstartedThreads = 0; //还未启动的线程
int nthreads; //ThreadGroup中线程的数量
Thread threads[]; //存放在当前线程组内的线程
int ngroups; //ThreadGroup中线程组的数量
ThreadGroup groups[]; //存放当前线程组内的线程组
}
由上可以看到,ThreadGroup不仅可以包含线程,还可包含线程组 。
2.构造函数
// 私有构造函数
private ThreadGroup() { // called from C code
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()方法,但是功能和ThreadGroup的不一样,它是用来确定当前运行的线程是否有权限修改自己。