6.ThreadGroup详细讲解

默认情况下, 新的线程都会被加入到main线程所在的group中, main线程的group名字同线程名。 如同线程存在父子关系一样,ThreadGroup同样也存在父子关系。

创建ThreadGroup

  • public ThreadGroup(String name)
  • public ThreadGroup(ThreadGroup parent, String nmae)
package com.shinley.concurrent.chapter6;

public class ThreadGroupCreator {
    public static void main(String[] args) {
        // 获取当前线程的ThreadGroup
        ThreadGroup currentGroup = Thread.currentThread().getThreadGroup();

        // 定义一个新的group
        ThreadGroup group1 = new ThreadGroup("Group1");

        // 输出为true
        System.out.println(group1 == currentGroup);

        // 定义group2, 指定group1为其父group
        ThreadGroup group2 = new ThreadGroup(group1,"group2");

        // 输出为true
        System.out.println(group2.getParent() == group1);
    }
}

复制Thread数组和ThreadGroup数组

ThreadGroup中的两个方法

  • public int enumerate(Thread[] list)
  • public int enumerate(Thread[] list, boolean recurse) 上面两个方法,会将ThreadGroup中的active线程全部复制到Thread数组中。
package com.shinley.concurrent.chapter6;

import java.util.concurrent.TimeUnit;

public class ThreadGroupEnumerateThreads {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个ThreadGroup
        ThreadGroup myGroup = new ThreadGroup("MyGroup");

        // 创建线程传入threadGroup
        Thread thread = new Thread(myGroup, () -> {
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "MyThread");
        thread.start();

        TimeUnit.MILLISECONDS.sleep(2);
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        Thread[] list = new Thread[mainGroup.activeCount()];

        int recurseSize = mainGroup.enumerate(list);
        System.out.println(recurseSize);

        recurseSize = mainGroup.enumerate(list, false);
        System.out.println(recurseSize);
    }
}

上面代码运行之后,最后一个输出会比第一个小1, 那是因为代码中将递归recurse设置为了false, MyGroup中的线程将不会包含在内。

复制ThreadGroup数组

package com.shinley.concurrent.chapter6;

import java.util.concurrent.TimeUnit;

public class ThreadGroupEnumerateThreadGroups {
    public static void main(String[] args) throws InterruptedException {
        ThreadGroup myGroup1 = new ThreadGroup("MyGroup1");
        ThreadGroup myGroup2 = new ThreadGroup(myGroup1,"mygroup2");

        TimeUnit.MILLISECONDS.sleep(2);
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();

        ThreadGroup[] list = new ThreadGroup[mainGroup.activeGroupCount()];

        int recurseSize = mainGroup.enumerate(list);
        System.out.println(recurseSize);

        recurseSize = mainGroup.enumerate(list, false);
        System.out.println(recurseSize);
    }
}

myGroup1的匀group为mainGroup, 而myGroup2的父group为myGroup1, 因此上述的代码运行之后,递归复制的结果为2, 不递归的情况下为1 。

ThreadGroup操作

  • activeCount() 获取group中活跃的线程
  • activeGroupCount() 获取group中活跃的子group
  • getMaxPriority() 获取group的优先级
  • getName 获取group的名字
  • getParent() 获取group的父group
  • list() 该方法没有返回值, 执行该方法会将group中所有活跃的线程信息打印到控制台
  • parentOf(ThreadGroup g) 判断当前group是不是给定group的父group
  • setMaxPriority(int pri) 设置group的最大优先级, 最大优先级不能超过父group的最大优先级, 执行该方法不仅会改变当前group的最大优先级, 还会改变所有子group的最大优先级。
package com.shinley.concurrent.chapter6;

import java.util.concurrent.TimeUnit;

public class ThreadGroupBasic {
    public static void main(String[] args) throws InterruptedException {
        ThreadGroup group = new ThreadGroup("group1");

        Thread thread = new Thread(group, ()-> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.setDaemon(true);
        thread.start();

        TimeUnit.MILLISECONDS.sleep(1);

        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();

        System.out.println("activeCount=" + mainGroup.activeCount());
        System.out.println("activeGroupCount="+ mainGroup.activeGroupCount());
        System.out.println("getMaxPriority=" + mainGroup.getMaxPriority());
        System.out.println("getName=" + mainGroup.getName());
        System.out.println("getParent=" + mainGroup.getParent());
        mainGroup.list();

        System.out.println("---------------");
        System.out.println("partOf=" + mainGroup.parentOf(group));
        System.out.println("parentOf=" + mainGroup.parentOf(mainGroup));
    }
}

如果把一个高优先级的线程,加入一个线程组以后, 再改变组的优先级小于线程的优先级,会怎样?

package com.shinley.concurrent.chapter6;

import java.util.concurrent.TimeUnit;

public class ThreadGroupPriority {
    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("group1");
        Thread thread = new Thread(group, ()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread");
        thread.setDaemon(true);
        thread.start();

        System.out.println("group.getMaxPriority()" + group.getMaxPriority());
        System.out.println("thread.getPriority()" + thread.getPriority());

        // 改变group的最大值
        group.setMaxPriority(3);

        System.out.println("group.getMaxPriority()" + group.getMaxPriority());
        System.out.println("thread.getPriority()" + thread.getPriority());
    }
}

打印

group.getMaxPriority()10
thread.getPriority()5
group.getMaxPriority()3
thread.getPriority()5

Process finished with exit code 0

改低组的最大优先组后, 已加入的线程的优先级不变, 后面加入的线程不会大于组的优先级

ThreadGroup的interrupt

interrupt一个group会导致该group中所有active线程都被interrupt, 也就是说该group中每一个线程的interrupt标识都被设置了。

package com.shinley.concurrent.chapter6;

import java.util.concurrent.TimeUnit;

public class ThreadGroupInterrupt {
    public static void main(String[] args) throws InterruptedException {
        ThreadGroup group = new ThreadGroup("TestGroup");

        new Thread(group, ()-> {
           while (true) {
               try {
                   TimeUnit.MILLISECONDS.sleep(2);
               } catch (InterruptedException e) {
                   break;
               }
           }
            System.out.println("t1 will exit");
        }, "t1").start();

        new Thread(group, () -> {
            while (true) {
                try {
                    TimeUnit.MILLISECONDS.sleep(1);
                } catch (InterruptedException e) {
                    break;
                }
            }
            System.out.println("t2 will exit");
        }, "t2").start();

        TimeUnit.MILLISECONDS.sleep(2);

        group.interrupt();
    }
}

打印

t2 will exit
t1 will exit

ThreadGroup的destroy

destroy用于销毁ThreadGroup, 该方法只是针对一个没有任何active线程的group进行一次destroy标记, 调用该方法的直接结果是在父group中将自已移除。

package com.shinley.concurrent.chapter6;

public class ThreadGroupDestroy {
    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("TestGroup");

        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        System.out.println("group.isDestroyed="+ group.isDestroyed());
        mainGroup.list();

        group.destroy();

        System.out.println("group.isDestroyed="+ group.isDestroyed());
        mainGroup.list();
    }
}

打印

group.isDestroyed=false
java.lang.ThreadGroup[name=main,maxpri=10]
    Thread[main,5,main]
    Thread[Monitor Ctrl-Break,5,main]
    java.lang.ThreadGroup[name=TestGroup,maxpri=10]
group.isDestroyed=true
java.lang.ThreadGroup[name=main,maxpri=10]
    Thread[main,5,main]
    Thread[Monitor Ctrl-Break,5,main]

守护ThreadGroup

线程可以设置为守护线程, ThreadGroup也可以设为守护ThreadGroup, 但是若将一个ThreadGroup设置为daemon, 也并不会影响线程的daemon属性, 如果一个ThreadGroup的daemon被设置为true, 那么在group中没有任何active线程的时候该group将自动destroy

package com.shinley.concurrent.chapter6;

import java.util.concurrent.TimeUnit;

public class ThreadGroupDaemon {
    public static void main(String[] args) throws InterruptedException {
        ThreadGroup group1 = new ThreadGroup("Group1");
        new Thread(group1, () -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "group1-thread").start();

        ThreadGroup group2 = new ThreadGroup("Group2");
        new Thread(group2, () -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "group2-thread2").start();

        group2.setDaemon(true);

        TimeUnit.SECONDS.sleep(3);

        System.out.println(group1.isDestroyed());
        System.out.println(group2.isDestroyed());
    }
}

打印

false
true