先贴上最原始的代码,疑惑的开始。
public class Solution {
public static void main(String[] args) {
T t1 = new T();
T t2 = new T();
t1.start();
System.out.println(Thread.activeCount());
t2.start();
}
static class T extends Thread{
@Override
public void run() {
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
上面的程序应该输出数字2?还是3?本着严谨的态度在ide上面跑了一下,本来以为是2,结果是3. t2的运行不是在打印之前嘛?为什么会是3呢?难道说t2也算进去了?继续去测试一下。
public static void main(String[] args) {
T t1 = new T();
T t2 = new T();
t1.start();
System.out.println(Thread.activeCount());
t2.start();
System.out.println(Thread.activeCount());
}
结果是3 4.这就说明另外一个活跃的线程不是它t2,另外有一个线程。顺着这个思路,去看看到底哪些线程在运行。
public static void main(String[] args) {
T t1 = new T();
T t2 = new T();
t1.start();
System.out.println(Thread.activeCount());
Map maps = Thread.getAllStackTraces();
for ( Thread thread:maps.keySet()){
System.out.println(thread.getName());
}
t2.start();
System.out.println(Thread.activeCount());
}
把所有的线程都打印出来看看。下面是运行结果。
1.png
运行结果让人大吃一惊,这是什么啊,不算t2都有7个活跃的进程,为什么Thread.activeCount()只有3或者4个呢?这是什么鬼哦。为了搞清楚到底是怎么回事,就去看看Thread.activeCount()的源码吧。下面展示的就是源码部分。
/**
* Returns an estimate of the number of active threads in the current
* thread's {@linkplain java.lang.ThreadGroup thread group} and its
* subgroups. Recursively iterates over all subgroups in the current
* thread's thread group.
*
*
The value returned is only an estimate because the number of
* threads may change dynamically while this method traverses internal
* data structures, and might be affected by the presence of certain
* system threads. This method is intended primarily for debugging
* and monitoring purposes.
*
* @return an estimate of the number of active threads in the current
* thread's thread group and in any other thread group that
* has the current thread's thread group as an ancestor
*/
public static int activeCount() {
return currentThread().getThreadGroup().activeCount();
}
看了源码,我们了解到原来Thread.activeCount()只是计算当前线程的线程组里面有多少活跃线程,而不是所有的活跃线程,这也就解释了,为什么会出现数目对不上的情况。接下来,我们看看每个线程组都是哪些线程。
public static void main(String[] args) {
T t1 = new T();
T t2 = new T();
t1.start();
System.out.println("当前活跃线程数量:"+Thread.activeCount());
ThreadGroup group = Thread.currentThread().getThreadGroup();
System.out.println("当前线程组的名称:"+group.getName());
Thread[] list1 = new Thread[group.activeCount()];
group.enumerate(list1);
for (Thread thread:list1){
System.out.println(thread.getName());
}
ThreadGroup groupParent = group.getParent();
System.out.println("当前线程组的名称:"+groupParent.getName());
Thread[] list2 = new Thread[groupParent.activeCount()];
groupParent.enumerate(list2);
for (Thread thread:list2){
System.out.println(thread.getName());
}
}
上面程序的运行结果如下图所示。
2.png
这次基本就清清楚楚,明明白白了。其中Thread-0就是我们的t1线程。接下来简单的描述一下其他的线程都是干什么的。参考http://ifeve.com/jvm-thread/
Attach Listener
Attach Listener线程是负责接收到外部的命令,而对该命令进行执行的并且吧结果返回给发送者。通常我们会用一些命令去要求jvm给我们一些反 馈信息,如:java -version、jmap、jstack等等。如果该线程在jvm启动的时候没有初始化,那么,则会在用户第一次执行jvm命令时,得到启动。
Signal Dispatcher
前面我们提到第一个Attach Listener线程的职责是接收外部jvm命令,当命令接收成功后,会交给signal dispather线程去进行分发到各个不同的模块处理命令,并且返回处理结果。signal dispather线程也是在第一次接收外部jvm命令时,进行初始化工作。
Finalizer
这个线程也是在main线程之后创建的,其优先级为10,主要用于在垃圾收集前,调用对象的finalize()方法;关于Finalizer线程的几点:
只有当开始一轮垃圾收集时,才会开始调用finalize()方法;因此并不是所有对象的finalize()方法都会被执行;
该线程也是daemon线程,因此如果虚拟机中没有其他非daemon线程,不管该线程有没有执行完finalize()方法,JVM也会退出;
JVM在垃圾收集时会将失去引用的对象包装成Finalizer对象(Reference的实现),并放入ReferenceQueue,由Finalizer线程来处理;最后将该Finalizer对象的引用置为null,由垃圾收集器来回收;
JVM为什么要单独用一个线程来执行finalize()方法呢?如果JVM的垃圾收集线程自己来做,很有可能由于在finalize()方法中误操作导致GC线程停止或不可控,这对GC线程来说是一种灾难;
Reference Handler
VM在创建main线程后就创建Reference Handler线程,其优先级最高,为10,它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。
Monitor Ctrl-Break
用于监控目的,监控Ctrl-Break中断信号的。