3.4 ThreadGroup

       正如ThreadGroup名字,表示一个线程组,可以将其理解成放一堆线程的容器。当然,ThreadGroup也可以存放其他的线程组。看下官方注释是怎么解释的吧!

* A thread group represents a set of threads. In addition, a thread
 * group can also include other thread groups. The thread groups form
 * a tree in which every thread group except the initial thread group
 * has a parent.
 * <p>
 * A thread is allowed to access information about its own thread
 * group, but not to access information about its thread group's
 * parent thread group or any other thread groups.

       线程组是树结构,可以容纳线程和线程组,每个线程组都有父节点,除了初始化线程组(可以理解成第一个创建的线程组)。线程可以获取到自己的线程组信息,但是不能获取其他线程组和父线程组信息。

3.4.1 ThreadGroup内部组成

private final ThreadGroup parent;
String name;
int maxPriority;
boolean destroyed;
boolean daemon;
boolean vmAllowSuspension;

int nUnstartedThreads = 0;
int nthreads;
//当前线程组的线程
Thread threads[];
int ngroups;
//树结构
ThreadGroup groups[];

       注意线程组里有Thread threads[]表示当前线程组管理的线程,ThreadGroup groups[]表示当前线程组的子线程组。

3.4.2 构造

/**
 * Creates an empty Thread group that is not in any Thread group.
 * This method is used to create the system Thread group.
 */
private ThreadGroup() {     // called from C code
    this.name = "system";
    this.maxPriority = Thread.MAX_PRIORITY;
    this.parent = null;
}

       空参构造ThreadGroup是一个私有构造方法,是C调用的方法,java无法调用该构造方法,注意system不是主线程所在线程组

System.err.println(Thread.currentThread().getThreadGroup().getName());

       查看当前线程所在线程组,在主线程中查看,可以看到主线程的线程组是main线程组。

3.4 ThreadGroup使用与源码解析_并发编程

3. ThreadGroup添加线程(不建议显示添加)

void add(Thread t) {
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            if (threads == null) {
            	//null默认4
                threads = new Thread[4];
            } else if (nthreads == threads.length) {
            	//扩容线程到原来两倍
                threads = Arrays.copyOf(threads, nthreads * 2);
            }
            threads[nthreads] = t;
            // This is done last so it doesn't matter in case the
            // thread is killed
            nthreads++;
            // The thread is now a fully fledged member of the group, even
            // though it may, or may not, have been started yet. It will prevent
            // the group from being destroyed so the unstarted Threads count is
            // decremented.
            nUnstartedThreads--;
        }
    }

4. ThreadGroup使用例子

package com.lbh.xxmanager.basic.java;
/**
 * Copyright(c)lbhbinhao@163.com
 *
 * @author liubinhao
 * @date 2021/1/30
 * ++++ ______                           ______             ______
 * +++/     /|                         /     /|           /     /|
 * +/_____/  |                       /_____/  |         /_____/  |
 * |     |   |                      |     |   |        |     |   |
 * |     |   |                      |     |   |________|     |   |
 * |     |   |                      |     |  /         |     |   |
 * |     |   |                      |     |/___________|     |   |
 * |     |   |___________________   |     |____________|     |   |
 * |     |  /                  / |  |     |   |        |     |   |
 * |     |/ _________________/  /   |     |  /         |     |  /
 * |_________________________|/b    |_____|/           |_____|/
 */
/**
 * 1.线程(线程的生命周期) —> 线程的用法(死锁:解决办法(Resource order),资源竞争Race condition)->线程通信(信号量:共享变量,管道就是|符号)
 * -> 线程池(ThreadGroup,ThreadLocal)-> 计算机线程(并发)模型 -> JVM(synchronized,volatile)
 * 2.并发 并发算法(阻塞和非阻塞) -> JUC(Java.util.concurrent大多数是阻塞)->(java.util:List,Queue,Set,Map)
 * ->(cpu底层提供的操作:Atomic) -> JVM(非阻塞的原理)
 */
class MultiThread implements Runnable{
    /**
     * public default protected private
     * @param args
     */
    public int item;

    public static void main(String[] args) throws InterruptedException {
        System.err.println(Thread.currentThread().getThreadGroup().getName());
        ThreadGroup threadGroup = new ThreadGroup("groupName");
        Thread thread = new Thread(threadGroup,new MultiThread());
        thread.start();
        Thread.sleep(100);
        System.err.println(threadGroup.activeCount());
        System.err.println(threadGroup);
    }
    @Override
    public void run() {
        System.err.println(Thread.currentThread());
        for (int i=0;i<5;i++){
            new Thread(()->{
                System.err.println(Thread.currentThread());
                while (true){
                    //doNothing
                }

            }
            ).start();
        }
        while(true){
            //doNothing
        }
    }
}

3.4 ThreadGroup使用与源码解析_ThreadGroup用法_02

ThreadGroup threadGroup = new ThreadGroup("groupName");
Thread thread = new Thread(threadGroup,new MultiThread());

       只往线程组中添加了一个创建的线程,但是最后线程组中却有6个,这是为什么呢?
       答案:当前线程创建的子线程会附加到当前线程的线程组,看源码(Thread类下)。

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */

        /* If there is a security manager, ask the security manager
           what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }

        /* If the security doesn't have a strong opinion of the matter
           use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }

    /* checkAccess regardless of whether or not threadgroup is
       explicitly passed in. */
    g.checkAccess();

    /*
     * Do we have the required permissions?
     */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
            acc != null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    tid = nextThreadID();
}
/* If the security doesn't have a strong opinion of the matter
   use the parent thread group. */
if (g == null) {
    g = parent.getThreadGroup();
}

        注意上边一段代码,构造传入的线程组为null时,获取到创建该线程的线程组,也就是说子线程会附加到创建该线程的线程组上。关于线程组还有很多用法,笔者就在此仅作抛砖引玉,欢迎与笔者讨论。