zoukankan      html  css  js  c++  java
  • java并发编程学习: 守护线程(Daemon Thread)

    在正式理解这个概念前,先把 守护线程 与 守护程 这二个极其相似的说法区分开,守护进程通常是为了防止某些应用因各种意外原因退出,而在后台独立运行的系统服务或应用程序。 比如:我们开发了一个邮件发送程序,一直不停的监视队列池,发现有待发送的邮件,就将其发送出去。如果这个程序挂了(或被人误操作关了),邮件就不发出去了,为了防止这种情况,再开发一个类似windows 系统服务的应用,常驻后台,监制这个邮件发送程序是否在运行,如果没运行,则自动将其启动。

    而我们今天说的java中的守护线程(Daemon Thread) 指的是一类特殊的Thread,其优先级特别低(低到甚至可以被JVM自动终止),通常这类线程用于在空闲时做一些资源清理类的工作,比如GC线程,如果JVM中所有非守护线程(即:常规的用户线程)都结束了,守护线程会被JVM中止,想想其实也挺合理,没有任何用户线程了,自然也不会有垃圾对象产生,GC线程也没必要存在了。

    实际开发中,也可以手动将线程设置为Daemon Thread,只有一个限制:必须在线程的start方法设置,见下面的示例:

    package test;
    
    public class Program {
    
        public static void main(String[] args) {
            TestThread t1 = new TestThread();
            t1.setDaemon(true);
            t1.start();
        }
    
        private static class TestThread extends Thread {
            public void run() {
                System.out.println("test");
            }
        }
    }
    

    由于t1设置成Daemon Thread了,运行后,main进程马上就结束,此时没有用户进程在运行,守护进程默认是不执行的,因此运行后,没有任何输出结果,符合我们刚才的解释。

    注:在idea等集成IDE环境下测试时,如果多次点击Run按钮,可能会发现第二次运行时,偶尔也会输出test,估计是ide里上次运行后的java进程并未完全退出,可以手动把windows进程中的所有java.exe进程干掉再测试。

    如果把t1.setDaemon(true);这一行注释掉,就会输出test了。

    另外,如果把main函数最后加一行阻塞的代码,比如:

        public static void main(String[] args) throws IOException {
            TestThread t1 = new TestThread();
            t1.setDaemon(true);
            t1.start();
            System.in.read();
        }
    

    加了一行System.in.read()后,再运行,会发现test会输出,这是因为main这个用户线程被阻塞了,JVM发现有用户进程在运行,守护进程才能机会被执行。

    再来一个复杂点的示例:

    假设有二个线程,一个是常规的用户线程,不停写入日志,另一个是守护线程,在空闲时清理日志(仅保留最近的5条日志)

    package test;
    
    import java.io.IOException;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.BlockingQueue;
    
    public class Program {
    
        private static int queueCapacity = 10;
        private static BlockingQueue<String> logQueue = new ArrayBlockingQueue<String>(queueCapacity);
    
        public static void main(String[] args) throws IOException {
    
            LogWriter writer = new LogWriter();
            LogCleaner cleaner = new LogCleaner();
            cleaner.setDaemon(true);
    
            writer.start();
            cleaner.start();
        }
    
        /**
         * 模拟不停写日志(直到队列写满)
         */
        private static class LogWriter extends Thread {
            public void run() {
                for (int i = 0; i < queueCapacity; i++) {
                    try {
                        logQueue.put("" + i);
                        System.out.println("日志已写入,当前日志内容:" + logQueue);
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    
        /**
         * 模拟在空闲时清理日志(仅保留5条日志)
         */
        private static class LogCleaner extends Thread {
            public void run() {
                while (true) {
                    if (logQueue.size() > 5) {
                        try {
                            logQueue.take();
                            System.out.println("多余日志被清理,当前日志内容:" + logQueue);
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    

    运行结果:

     1 日志已写入,当前日志内容:[0]
     2 日志已写入,当前日志内容:[0, 1]
     3 日志已写入,当前日志内容:[0, 1, 2]
     4 日志已写入,当前日志内容:[0, 1, 2, 3]
     5 日志已写入,当前日志内容:[0, 1, 2, 3, 4]
     6 日志已写入,当前日志内容:[0, 1, 2, 3, 4, 5]
     7 多余日志被清理,当前日志内容:[1, 2, 3, 4, 5]
     8 日志已写入,当前日志内容:[1, 2, 3, 4, 5, 6]
     9 多余日志被清理,当前日志内容:[2, 3, 4, 5, 6]
    10 日志已写入,当前日志内容:[2, 3, 4, 5, 6, 7]
    11 多余日志被清理,当前日志内容:[3, 4, 5, 6, 7]
    12 日志已写入,当前日志内容:[3, 4, 5, 6, 7, 8]
    13 多余日志被清理,当前日志内容:[4, 5, 6, 7, 8]
    14 日志已写入,当前日志内容:[4, 5, 6, 7, 8, 9]
    15 多余日志被清理,当前日志内容:[5, 6, 7, 8, 9]

    参考文章:

    http://ifeve.com/thread-management-8/

    http://www.cnblogs.com/super-d2/p/3348183.html

  • 相关阅读:
    html基础起航
    必杀技———SQL基础整理系列(一)
    JavaScript代码段整理笔记系列(一)
    与JSP的初次邂逅……
    产品第二篇
    产品第一篇
    进程在后台可靠运行的几种方法
    Vue.js模板语法
    更靠谱的横竖屏检测方法
    浮动【电梯】或【回到顶部】小插件:iElevator.js
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/daemon-thread-demo.html
Copyright © 2011-2022 走看看