首页 > 其他分享 >多线程系列(十七) -线程组介绍

多线程系列(十七) -线程组介绍

时间:2024-03-12 14:47:19浏览次数:31  
标签:十七 currentThread Thread ThreadGroup 线程 void 多线程 public

一、简介

在之前的多线程系列文章中,我们陆陆续续的介绍了Thread线程类相关的知识和用法,其实在Thread类上还有一层ThreadGroup类,也就是线程组。

今天我们就一起来简单的聊聊线程组相关的知识和用法。

二、什么是线程组

线程组,简单来说就是多个线程的集合,它的出现主要是为了更方便的管理线程。

从结构角度看,线程组与线程之间其实是一个父子结构,一个线程组可以拥有几个线程,同时也可以拥有几个线程组。整个组织结构像一棵树一样,每个线程一定有一个线程组,线程组可能又有一个父线程组,追溯到根节点就是一个系统线程组。

线程组与线程之间的关系,可以用如下图来描述。

比如,我们通常创建的main方法,对应的是main线程,它所属的是main线程组,main线程组的父级是是system系统线程组。

public static void main(String[] args) {
    Thread currentThread = Thread.currentThread();
    ThreadGroup currentThreadGroup = currentThread.getThreadGroup();
    ThreadGroup systemThreadGroup = currentThreadGroup.getParent();
    System.out.println("currentThread:" + currentThread.getName());
    System.out.println("currentThreadGroup:" + currentThreadGroup.getName());
    System.out.println("systemThreadGroup:" + systemThreadGroup.getName());
}

输出结果如下:

currentThread:main
currentThreadGroup:main
systemThreadGroup:system

其中system线程组就是根节点,再上一层就没有了,如果调用会抛空指针异常。

线程组最主要的作用是:可以实现批量管理线程或者线程组,有效的对线程或者线程组对象进行检查、尝试中断等操作。

下面我们就一起来看看ThreadGroup的常用方法和使用技巧。

三、线程组用法详解

3.1、构造方法介绍

ThreadGroup提供了两个构造方法,内容如下:

方法 描述
ThreadGroup(String name) 根据线程组名称创建线程组,其父线程组为main线程组
ThreadGroup(ThreadGroup parent, String name) 根据线程组名称创建线程组,其父线程组为指定的 parent 线程组

其中支持指定父级线程组的方法,在实际的使用中比较常见。

下面,我们演示一下这两个构造函数的用法:

public static void main(String[] args) {
    ThreadGroup subThreadGroup1 = new ThreadGroup("sub1");
    ThreadGroup subThreadGroup2 = new ThreadGroup(subThreadGroup1, "sub2");
    System.out.println("sub1 parent thread group name:" + subThreadGroup1.getParent().getName());
    System.out.println("sub2 parent thread group name:" + subThreadGroup2.getParent().getName());
}

输出结果如下:

sub1 parent thread group name:main
sub2 parent thread group name:sub1

3.2、核心方法介绍

ThreadGroup提供了很多有用的方法,下面整理了一些方法的简要介绍,内容如下:

方法 描述
public final String getName() 返回此线程组的名称
public final ThreadGroup getParent() 返回此线程组的父级
public final boolean parentOf(ThreadGroup g) 测试此线程组是线程组参数还是其父级线程组之一
public int activeCount() 返回此线程组及其子组中活动线程的数量的估计值,递归遍历该线程组中所有的子组,此方法主要用于调试和监视目的
public int activeGroupCount () 返回此线程组及其子组中活动组的数目的估计值。递归遍历该线程组中的所有子群,此方法主要用于调试和监视目的
public final void checkAccess() 确定当前运行的线程是否具有修改此线程组的权限
public int enumerate(Thread[] list) 将这个线程组复制到它所在的组及其子组中
public final void destroy() 销毁此线程组及其所有子组,当线程组还要子线程或者子线程组,会抛异常
public boolean isDestroyed() 测试此线程组是否已被销毁
public final int getMaxPriority() 返回此线程组的最大优先级
public final void setMaxPriority(int pri) 设置组的最大优先级。线程组中具有较高优先级的线程不会受到影响
public final boolean isDaemon() 测试此线程组是否是守护线程组
public final void setDaemon(boolean daemon) 修改此线程组的守护进程状态
public final void interrupt() 尝试中断此线程组中的所有线程
public void list() 将此线程组的信息打印到标准输出。此方法仅用于调试

下面我们抽取几个比较常见的方法,进行演示介绍。

3.2.1、activeCount 方法

activeCount()方法用于返回此线程组及其子组中活动线程的数量的估计值,因为线程的数量是动态发生变化的,返回的值只是一个估计值。

我们看一个简单的例子就知道了。

public class MyThread extends Thread{

    public MyThread(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class MyThreadMainTest {

    public static void main(String[] args) throws Exception {
        ThreadGroup tg = new ThreadGroup("group1");
        MyThread t1 = new MyThread (tg, "t1");
        MyThread t2 = new MyThread (tg, "t2");
        t1.start();
        t2.start();

        System.out.println("线程组的名称:" +  tg.getName() + ",活动的线程数:" +  tg.activeCount());
        Thread.sleep(1000);
        System.out.println("线程组的名称:" +  tg.getName() + ",活动的线程数:" +  tg.activeCount());
    }
}

输出结果如下:

线程组的名称:group1,活动的线程数:2
线程组的名称:group1,活动的线程数:0

第一次检查线程都处于运行状态,因此活动的线程数为 2;过 1 秒之后,线程运行结束,活动的线程数为 0。

3.2.2、isDaemon 方法

setDaemon()方法用于测试此线程组是否是守护线程组。

需要注意的是:后台线程组和后台线程是两个概念,后台线程组的特性是最后一个线程执行完或最后一个线程被销毁时,后台线程组自动销毁,线程组只是为了统一管理线程的一个方式,跟后台线程有区别!

例子如下:

public class MyThread extends Thread{

    public MyThread(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        System.out.println("当前线程:" + Thread.currentThread().getName() + ",是否后台线程:" +  Thread.currentThread().isDaemon());
        System.out.println("当前线程组:" + Thread.currentThread().getThreadGroup().getName() + ",是否后台线程组:" +  Thread.currentThread().getThreadGroup().isDaemon());
    }
}

public class MyThreadMainTest4 {

    public static void main(String[] args) throws Exception {
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        new MyThread(mainGroup, "t1").start();

        Thread.sleep(100);

        // 设置守护线程组
        ThreadGroup tg = new ThreadGroup("group1");
        tg.setDaemon(true);
        new MyThread(tg,"t2").start();
    }
}

输出结果如下:

当前线程:t1,是否后台线程:false
当前线程组:main,是否后台线程组:false
当前线程:t2,是否后台线程:false
当前线程组:group1,是否后台线程组:true
3.2.3、interrupt 方法

interrupt()方法用于尝试中断此线程组中的所有线程。如果正在运行的线程没有进入阻塞,是无法中断的。

例子如下:

public class MyThreadA extends Thread{

    public MyThreadA(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        System.out.println("线程:" + Thread.currentThread().getName() + ",开始运行");
        String t;
        for (int i = 0; i < 1000000000; i++) {
            t = i + "";
        }
        System.out.println("线程:" + Thread.currentThread().getName() + ",停止运行");
    }
}
public class MyThreadB extends Thread{

    public MyThreadB(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        System.out.println("线程:" + Thread.currentThread().getName() + ",开始运行");
        while (!Thread.interrupted()){
        }
        System.out.println("线程:" + Thread.currentThread().getName() + ",停止运行");
    }
}
public class MyThreadC extends Thread{

    public MyThreadC(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        System.out.println("线程:" + Thread.currentThread().getName() + ",开始运行");
        try {
            Thread.sleep(1000);
        } catch (Exception e){
//            e.printStackTrace();
        }
        System.out.println("线程:" + Thread.currentThread().getName() + ",停止运行");
    }
}
public class MyThreadMainTest {

    public static void main(String[] args) throws Exception {
        ThreadGroup tg = new ThreadGroup("group1");
        new MyThreadA(tg,"t1").start();
        new MyThreadB(tg,"t2").start();
        new MyThreadC(tg,"t3").start();

        // 尝试中断线程组里面的线程
        tg.interrupt();
    }
}

输出结果如下:

线程:t1,开始运行
线程:t2,开始运行
线程:t2,停止运行
线程:t3,开始运行
线程:t3,停止运行

线程t1只有等它运行结束,通过interrupt()不能中断程序!

四、小结

本文主要围绕线程组的一些基本概念以及常用方法,并结合了一些简单示例进行介绍。

线程组的出现更多的是便于有组织的管理线程,比如 Java 的线程池就用到了线程组,更多的线程知识,我们在后续的文章中会进行介绍。

如果有描述不对的地方,欢迎网友留言指出。

五、参考

1、https://www.cnblogs.com/xrq730/p/4856072.html

2、https://cloud.tencent.com/developer/article/1633465

标签:十七,currentThread,Thread,ThreadGroup,线程,void,多线程,public
From: https://www.cnblogs.com/dxflqm/p/18068251

相关文章

  • 深入浅出Java多线程(十一):AQS
    引言大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第十一篇内容:AQS(AbstractQueuedSynchronizer)。大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!!在现代多核CPU环境中,多线程编程已成为提升系统性能和并发处理能力的关键手段。然而,当多个线程共享同一资源......
  • 自己简单实现一个线程池
    线程池Java中的线程池是运用最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。线程池的好处/作用:1.不需要频繁的创建和销毁线程,提高相应速度2.管理线程,避免无休止的创建线程导致资源枯竭。(不会每来一个任务就创建一个线程,线程不断的取任务执行)线程池......
  • C#3种常见的定时器(多线程)
    总结以下三种方法,实现c#每隔一段时间执行代码:方法一:调用线程执行方法,在方法中实现死循环,每个循环Sleep设定时间;调用线程执行方法,在方法中实现死循环,每个循环Sleep设定时间Threadthread=newThread(newThreadStart(obj.Method1));thread.Start();方法二:使用System.Timers......
  • 多线程
    多线程的实现java.lang.Thread类代表多线程注意事项启动线程必须是start方法,不是调用run方法不要把主线任务放在启动子线程之前继承Thread/***@authorPickle*@versionV1.0*@date2024/3/1116:43*/publicclassMyThreadextendsThread{@Override......
  • HashMap线程不安全实例(jdk8)
    一、前言本文紧接:HashMap线程不安全实例(jdk1.7)-seeAll-博客园(cnblogs.com),介绍jdk8中线程不安全的一些情况,且主要是在上篇文章的基础上和jdk1.7做一个对比。 二、初始化桶数组的例子1,测试代码和上篇文章一样。2,断点设置同样设置在初始化桶数组的地方,且断点的详细配......
  • t01_多线程
    进程与线程进程:进程是程序的基本执行实体线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位在Java中,进程(Process)和线程(Thread)是两个并发执行的概念,用于实现多任务处理和并发执行。它们都是操作系统和编程语言级别的概念,用于管理和执......
  • Java高并发讲解:守护线程——在源代码中分析setDaemon()
    Java高并发讲解:守护线程——在源代码中分析setDaemon()提出问题我们都知道Java线程分为主线程和守护线程,守护线程是需要手动指定的(setDaemon(true)......
  • Spring多线程事务处理
    一、背景本文主要介绍了spring多线程事务的解决方案,心急的小伙伴可以跳过上面的理论介绍分析部分直接看最终解决方案。在我们日常的业务活动中,经常会出现大规模的修改插入操作,比如在3.0的活动赛事创建,涉及到十几张表的插入(一张表可能插入一行或者多行数据),由于单线程模型的关系,......
  • 面试官:说说线程池的工作原理?
    线程池的底层是基于线程和任务队列来实现的,创建线程池的创建方式通常有以下两种:普通Java项目,使用ThreadPoolExecutor来创建线程池,这点《阿里巴巴Java开发手册》中也有说明,如下图所示:Spring项目中,会使用代码可读性更高的ThreadPoolTaskExecutor来创建线程池,虽然它的......
  • Java多线程基础用法
    线程创建线程创建的三种方式:Thread(继承Thread类)自定义线程类继承Thread类重写run()方法。编写线程执行体创建线程对象,调用start()方法启动线程packagecom.lily.demo01;publicclassTestThreadextendsThread{@Overridepublicvoidrun(){for......