1 线程组概念
Java中提供了线程组类: java.lang.ThreadGroup, ThreadGroup线程组是为了方便管理线程,它可以统一设定线程组中的线程的优先级,或者设置为守护线程,或者为没有设置异常处理器的线程设置统一的异常处理器等等,同时也可以通过线程组方便的获的线程组中的线程的一些信息。
通过查看Thread线程类的构造方法,可以发现在实例化线程对象的时候,可以指定所属的线程组,Thread(ThreadGroup group,...)。如果创建线程的时候没有指定线程组,那么该线程将会默认自动属于创建该线程的线程所在的组,例如在main方法中创建的线程,如果没有指定线程组,那么将会属于main线程所在的线程组(main线程组)。一旦一个线程归属到一个线程组之中后,就不能再更换其所在的线程组。并且只有当调用start()方法启动线程的时候,才真正的把线程加入到了线程组中。
每个线程组ThreadGroup 又可以包含一组线程和一组线程组,所以线程组是以树状形式存在,通常情况下,最顶层的根线程组是system线程组,system线程组下是main线程组,所以在main方法中创建的线程如果没有指定线程组,那么它将属于main线程组,如果指定了线程组(并且没有设置父线程组),那么它的父线程组将会是main线程组。
2 线程组API(部分....)
ThreadGroup(String name),构造方法1,父线程组将会默认是创建该线程的线程所属的线程组。
ThreadGroup(ThreadGroup parent, String name) 构造方法2,指定了父线程组。
int activeCount() 返回此线程组中活动线程的估计数。 注意。默认情况 下,其子线程组也会进行计算。
int activeGroupCount() 返回此线程组中活动子线程组的估计数。
void checkAccess() 确定当前运行的线程是否有权修改此线程组。 如果调用线程没有权限修改线程组抛出SecurityException。
void destroy() 销毁此线程组及其所有子组。
void interrupt() 中断此线程组中的所有线程。
int enumerate(Thread[] list) 把此线程组及其子组中的所有活动线程复制到指定数组list中。
int enumerate(Thread[] list, boolean recurse) 把此线程组中的所有活动线程复制到指定数组中。 如果recurse为true,则还要将子线程组中的活动线程一起复制 即int enumerate(Thread[] list)。
int enumerate(ThreadGroup[] list) 把此线程组中的所有活动线程组及其子线程组的引用复制到指定数组中。
int enumerate(ThreadGroup[] list, boolean recurse) 如果recurse为true,同enumerate(ThreadGroup[] list)。
void setDaemon(boolean daemon) 更改此线程组的后台程序状态。
void setMaxPriority(int pri) 设置线程组的最高优先级。
void uncaughtException(Thread t, Throwable e) 当此线程组中的线程因为一个未捕获的异常而停止,并且线程自身没有指定特定 Thread.UncaughtExceptionHandler 时,由JVM调用此方法。
public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else { Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null) { ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { System.err.print("Exception in thread \"" + t.getName() + "\" "); e.printStackTrace(System.err); } } }
线程组中的线程出现运行时异常会首先调用线程自己的异常处理器(Thread线程类也有setUncaughtExceptionHandler方法可以设置异常处理器),如果线程自身没有定义则会调用线程组的异常处理器,即默认情况下允许上面的代码:
如果父线程组存在, 则调用它的uncaughtException方法.
如果父线程组不存在, 但指定了默认处理器Thread.getDefaultUncaughtExceptionHandler, 则调用默认的处理器。
如果默认处理器没有设置, 则写错误日志.但如果 exception是ThreadDeath实例的话,则忽略。
3 线程组结论
线程组存储了线程对象和关联的线程组对象,并可以访问它们的信息(例如状态),将执行操作应用到所有成员上(例如中断)。
线程组和线程池的区别
线程组和线程池是两个不同的概念,他们的作用完全不同,前者是为了方便线程的管理,后者是为了管理线程的生命周期,复用线程,减少创建销毁线程的开销。
后记
java程序启动至少会启动几个线程?
一、利用ThreadGroup获得结果
public class ThreadDemo { public static void main(String[] args) { ThreadGroup g = Thread.currentThread().getThreadGroup(); while (g != null) { ThreadGroup temp = g.getParent(); if (temp == null) { break; } g = temp; } // 现在g就是根线程组 System.out.println("active count is " + g.activeCount()); Thread[] all = new Thread[g.activeCount()]; g.enumerate(all); for (Thread t : all) { System.out.println(t.getName()); } } }
二、利用ThreadMXBean获得结果
public class ThreadNumDemo { public static void main(String[] args) { ThreadMXBean threadMXBean =ManagementFactory.getThreadMXBean(); ThreadInfo[] threadInfos=threadMXBean.dumpAllThreads(false,false); for (ThreadInfo threadInfo : threadInfos) { System.out.println(threadInfo.getThreadId()+"-"+threadInfo.getThreadName()); } } }
以上两种方式获得结果应该是一致的,如果JDK版本一致的话。不同的JDK版本得出的结果可能是不同的。在JDK1.7上的结果为5个:
1. Reference Handler //清除reference的线程
2. Finalizer //调用对象的finalize方法的线程,就是垃圾回收的线程
3. Signal Dispatcher //分发处理发送给JVM信号的线程
4. Attach Listener
5. main //主线程
这5个线程除了main是我们自己代码run所在的线程,其他都是虚拟机启动的线程。