线程组(ThreadGroup)
在一个系统中,如果线程数量很多,而且功能分配比较明确,就可以将相同功能的线程放置在同一个线程组里。下面通过示例来说明。
public class ThreadGroupName implements Runnable {
public static void main(String[] args){
ThreadGroup tg = new ThreadGroup("PringGroup");
Thread t1 = new Thread(tg, new ThreadGroupName(), "T1");
Thread t2 = new Thread(tg, new ThreadGroupName(), "T2");
t1.start();
t2.start();
System.out.println(tg.activeCount());
tg.list();
}
@Override
public void run(){
String groupAndName = Thread.currentThread().getThreadGroup().getName() + "-" + Thread.currentThread().getName();
while (true) {
System.out.println("I am " + groupAndName);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
上述代码中,第4行建立了一个名为“PrintGroup”的线程组,并将T1和T2两个线程加入到此线程组中。第5、6两行创建了两个线程,使用Thread的构造函数,指定了线程所属的线程组,将线程和线程组关联起来。activeCount()方法可以获得活动线程的总数。不过线程是动态的,因此这个值只是一个估计值,无法精确。list()方法打印这个线程组中所有的线程信息,对调试有一定帮助。打印结果如下:
线程组还有一个值得注意的方法stop(),它会停止线程组中所有的线程。看起来是一个很方便的方法,但它会遇到和Thread.stop()方法同样的问题,因此使用时需要格外谨慎。
另外,提一下编码习惯。在创建线程和线程组的时候,建议取一个好听名字。对计算机来说不重要,但在系统出现问题时,很可能需要知道系统内的所有线程,如果拿到的是一连串的Thread-0、Thread-1、Thread-2,一定会头疼。而如果看到的是类似HttpHandler、FTPService这样的名字,则会顺心很多。
守护线程(Daemon)
守护线程是一种特殊的线程,就像它的名字一样,它是系统的守护者。在后台默默的完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程。与之相应的是用户线程,用户线程可以认为是系统的工作线程,它会完成这个程序应该要完成的业务操作。如果用户线程全部结束,则意味着这个程序实际上无事可做了。守护线程要守护的对象已经不存在了,那么整个应用程序就应该结束。因此一个Java应用内只有守护线程时,Java虚拟机就会自然退出。
下面看一个简单的守护线程的应用。
public class DaemonDemo {
public static void main(String[] args) throws InterruptedException {
Thread t = new DaemonT();
// 将线程t设置为守护线程,注意这一步要在start()方法之前设置
t.setDaemon(true);
t.start();
Thread.sleep(2000);
}
public static class DaemonT extends Thread{
@Override
public void run(){
while (true){
System.out.println("I am alive");
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
上述代码中,第6行将线程t设置为守护线程。需要注意的是,设置守护线程必须在start()方法之前设置。否则出抛出下面的异常,告诉你守护线程设置失败。不过程序和线程依然可以正常执行,从打印的结果中也看到了,还是打印了“I am alive”。只不过被当作用户线程而已。因此如果不小心忽略了下面的异常信息,很可能察觉不到这个错误,就会诧异为什么程序永远停不下来呢?
在这个例子中,由于线程t被设置为守护线程,系统中只有主线程main为用户线程,因此在main线程休眠2秒钟后退出时,整个程序也随之结束了。但如果不把线程t设置为守护线程,那main线程结束后,t线程还会不停地打印,永远也不会结束。正确的结果打印如下:
注:以上内容参考《实战Java高并发程序设计(第2版)》。