首尔国立大学(韩国)校训:“真理是我的光明。”
生活的前方不管有多少荆棘,我都随身携带着镰刀,砥砺前行;烈日高阳,打一罐鸡血,开启一天的新生活!
前面几篇聊了很多线程的基础知识,今天讲下线程的家族式管理ThreadGroup,类似中国的宗族制,每个人都有隶属的一脉,绝不可能是游离的;同样,jvm管理线程也采用了这种思想,每个线程也绝不会是游离的,都有隶属的ThreadGroup。可能大家在工作中没有注意到它的存在,但在某些场景下还是很有用途的。下面就从线程组的基本概念/管理结构/使用场景简单聊下:
一 概述线程组
线程组管理线程是一种组织式地管理,类似与公司的组织架构管理,要注意与线程池管理线程进行区分;它是按一定层级结构管理线程或线程组对象,线程和线程组构成的结构是树形,可以通过递归遍历的方式获取整个线程树。线程组有几个重要特性,如下:
1/自动归属,即线程或线程组具有默认机制,通俗地说,创建一个线程或线程组,它必有自己的所属的线程组,每个线程都不可能是游离的,这可以通过查看new Thread()和new ThreadGroup()的源码查阅便知。
2/多级关联,父对象中有子对象,子对象可创建子对象,子子孙孙无穷尽也,这是基于jdk提供的线程树结构,但在实际开发中,都是以简单易读地方式管理线程,所以推荐一级关联。
3/一级关联,父对象中有子对象,但不能创建孙对象,即树形结构只有两层,这在实际工作中大部分场景已经够用,组织结构越简单,自然管理也就越简单,故在工作中推荐使用。
demo世界不孤单,请阅:
/**
* @author 阿伦故事
* @Description:描述线程组的基本属性
* */
@Slf4j
public class GroupBasic {
public static void main(String[] args) {
GroupBasic groupBasic = new GroupBasic();
//auto
groupBasic.auto();
//one level
groupBasic.oneLevel();
//multi level
groupBasic.multiLevel();
}
/**
* 自动归属
* */
public void auto(){
Thread current = Thread.currentThread();
Thread thread = new Thread(()->{
log.info("---run---");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
log.info("--current thread name:"+current.getName() + ",线程组:"+current.getThreadGroup().getName());
log.info("--new thread name:"+thread.getName() + ",线程组:"+thread.getThreadGroup().getName());
}
/**
* 一级关联
* */
public void oneLevel(){
ThreadGroup group = new ThreadGroup("一级线程组");
Thread t1 = new Thread(()->{
log.info("---t1 run---");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(()->{
log.info("---t2 run---");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread one = new Thread(group, t1);
Thread two = new Thread(group, t2);
one.start();
two.start();
log.info("--ThreadGroup name:"+group.getName() + ",存活线程个数:"+group.activeCount());
}
/**
* 多级关联
* */
public void multiLevel(){
ThreadGroup group = new ThreadGroup("一级线程组");
ThreadGroup group1 = new ThreadGroup(group,"二级线程组A");
ThreadGroup group2 = new ThreadGroup(group,"二级线程组B");
Thread t1 = new Thread(()->{
log.info("---t1 run---");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(()->{
log.info("---t2 run---");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t3 = new Thread(()->{
log.info("---t3 run---");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread one = new Thread(group, t1);
Thread two = new Thread(group1, t2);
Thread three = new Thread(group2, t3);
one.start();
two.start();
three.start();
log.info("--一级线程组 name:"+group.getName() + ",存活线程个数:"+group.activeCount()
+",子线程组个数:"+group.activeGroupCount());
log.info("--二级线程组A name:"+group1.getName() + ",存活线程个数:"+group1.activeCount()
+",子线程组个数:"+group1.activeGroupCount());
log.info("--二级线程组B name:"+group2.getName() + ",存活线程个数:"+group2.activeCount()
+",子线程组个数:"+group2.activeGroupCount());
}
}
二 线程批管理
线程组在实战中有什么用呢,主要用对一批线程进行管理,如中断(interrupt)/暂停(suspend)/恢复(resume)/终止(stop)等操作,这里注意下ThreadGroup的destroy方法,是清除掉这个线程组及其所有子线程组,但要注意这里的所有线程组包括子线程组必须是空的,也就是说线程组里的线程都销亡了,否则会抛出异常。
这里主要讲下实战中的使用场景:
1/在单个应用中,使用多线程开发,必然声明线程池管理线程,建议根据业务模块声明相应的线程组来管理,便于针对某一业务模块的线程做统一处理;
2/在任务调度模块中,声明多个耗时任务执行数据处理,如果定时任务长时间未执行完毕,可能是由于执行过程中出现了异常等,这时需要把这类任务终止,就可以利用线程组把一类线程中断掉。
demo世界不孤单,请阅:
/**
* @author 阿伦故事
* @Description:描述线程组批处理
* */
@Slf4j
public class GroupBatch {
public static void main(String[] args) throws InterruptedException{
GroupBatch groupBatch = new GroupBatch();
groupBatch.batchTest();
}
/**
* 线程组下的线程任务执行异常,需要全部终止
* */
public void batchTest() throws InterruptedException{
ThreadGroup group = new ThreadGroup("批处理线程组");
for (int i = 0; i <5 ; i++) {
new Thread(group, new SubTask(),"subtask-0"+i).start();
}
Thread.sleep(5000);
log.info("--all SubTask 阻塞,需终止--");
group.interrupt();//中断线程组下的所有线程任务
Thread.sleep(2000);
log.info("--stop the world--");
}
static class SubTask implements Runnable{
@Override
public void run() {
log.info("--subTask thread name:"+ Thread.currentThread().getName() + ",begin run");
while(!Thread.currentThread().isInterrupted()){ };
log.info("--subTask thread name:"+ Thread.currentThread().getName() + ",end run");
}
}
}
特此声明:
分享文章有完整的知识架构图,将从以下几个方面系统展开:
1 基础(Linux/Spring boot/并发)
2 性能调优(jvm/tomcat/mysql)
3 高并发分布式
4 微服务体系
如果您觉得文章不错,请关注阿伦故事,您的支持是我坚持的莫大动力,在此受小弟一拜!
每篇福利: