线程组与线程池
以前接触学习线程组时,并没有太深入去了解其中的东西,原因是当初和同学讨论赛车游戏中,倒计时出发场景,是怎么实现多个线程同时启动的?所以就转去学习线程池了。不过后来发现其实是可以用CountDownLatch或者CyclicBarrier - -、。现在重新拾起了线程组,决定写篇日志,总结一些线程组相关。
创建线程池的目的,相信大家都很熟悉,就是为了解决许多的线程频繁创建和销毁,需要花费大量时间而导致效率不佳和资源耗尽利用问题。方法是在线程池中,始终存在一些活跃的线程,如果需要使用线程,就从线程池中取出,用完后把线程放回到线程池里,在需要时再唤醒。
创建线程组的目的,更多的是为了管理线程,以及线程安全问题。管理线程用分类的思想,通常将一些功能相同或相似的线程放到一个线程组中一起管理,例如数据库连接线程和数据库操作线程等。至于安全问题,保证线程数据安全的做法是,对于在同一个线程组内的线程可以互相修改其他线程的数据,而处于不同线程组中的线程,不能修改互相的数据。
这么看来,线程组和线程池的有一共同过作用都是可以用来管理线程,不同的地方有,线程池中不能放入线程池,也就是不能嵌套线程池,但是在线程组中可以嵌套放入线程组,这样一来,线程组不光可以统一管理线程,也可以管理放在它里面的线程组。接下来就总结一些关于线程组相关的特性,看看它与线程池还有哪一些区别。
ThreadGroup源码
回想之前的自定义线程池,想要知道线程池里有些什么属性,是通过看它的构造方法public ThreadPoolExecutor();里面有指定线程池中线程的数量、最大线程数、空闲线程存活时间,任务队列以及拒绝服务等属性,我们想要自定义线程池,也是通过修改构造方法中的参数来实现。同样,想了解线程组里面有些什么属性,先来看看它的构造方法中都有些什么。在Java中用ThreadGroup类来描述,它有两个public的构造方法:
public ThreadGroup(String name) {
this(Thread.currentThread().getThreadGroup(), name);
}
public ThreadGroup(ThreadGroup parent, String name) {
this(checkParentAccess(parent), parent, name);
}
这两个构造方法用来给我们初始化线程组用,从中看不出太多的属性,只有设置线程组名和它的父线程组。ThreadGroup类中还有一个私有的构造方法,它里面才包含了线程组中的一些关键属性:
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
private final ThreadGroup parent;
String name;
int maxPriority;
boolean destoryed;
boolean daemon;
boolean vmAllowSuspension;
int nUnstartedThreads = 0;
int nthread;
Thread threads[];
int ngroups;
ThreadGroup groups[];
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);
}
}
可以看到,在一个线程组中,除了有线程组名,父线程组外,还有线程组的最大允许优先级,是否为守护线程,以及线程组的子线程组。线程组中保存线程使用了数组threads,保存子线程组也一样是数组形式groups。这也证明了,线程组中既可以存放线程实例对象,也可以存放线程组。我们可以写些简单的代码,看看这些线程组中的关联关系。
线程组的默认归属
在检验测试线程组的关联关系前,先了解一个重要的特性,就是线程组的“默认归属”特性。什么是默认归属?指的是新建线程组时,如果没有指定线程组的父线程组,那么该线程组会默认自动归属到当前线程的线程组中。来看个例子:
public class ThreadGroupTest {
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("