Java并发基础(八)——线程组(ThreadGroup)与守护线程(Daemon)

线程组(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()方法打印这个线程组中所有的线程信息,对调试有一定帮助。打印结果如下:

Java并发基础(八)——线程组(ThreadGroup)与守护线程(Daemon)_第1张图片

        线程组还有一个值得注意的方法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”。只不过被当作用户线程而已。因此如果不小心忽略了下面的异常信息,很可能察觉不到这个错误,就会诧异为什么程序永远停不下来呢?

Java并发基础(八)——线程组(ThreadGroup)与守护线程(Daemon)_第2张图片

        在这个例子中,由于线程t被设置为守护线程,系统中只有主线程main为用户线程,因此在main线程休眠2秒钟后退出时,整个程序也随之结束了。但如果不把线程t设置为守护线程,那main线程结束后,t线程还会不停地打印,永远也不会结束。正确的结果打印如下:

 

注:以上内容参考《实战Java高并发程序设计(第2版)》。

你可能感兴趣的:(多线程与高并发)