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

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
10天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
10天前
|
安全 Java 开发者
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第9天】本文将深入探讨Java并发编程的核心概念,包括线程安全和性能优化。我们将详细解析Java中的同步机制,包括synchronized关键字、Lock接口以及并发集合等,并探讨它们如何影响程序的性能。此外,我们还将讨论Java内存模型,以及它如何影响并发程序的行为。最后,我们将提供一些实用的并发编程技巧和最佳实践,帮助开发者编写出既线程安全又高效的Java程序。
22 3
|
10天前
|
算法 Java 开发者
Java中的多线程编程:概念、实现与性能优化
【4月更文挑战第9天】在Java编程中,多线程是一种强大的工具,它允许开发者创建并发执行的程序,提高系统的响应性和吞吐量。本文将深入探讨Java多线程的核心概念,包括线程的生命周期、线程同步机制以及线程池的使用。接着,我们将展示如何通过继承Thread类和实现Runnable接口来创建线程,并讨论各自的优缺点。此外,文章还将介绍高级主题,如死锁的预防、避免和检测,以及如何使用并发集合和原子变量来提高多线程程序的性能和安全性。最后,我们将提供一些实用的性能优化技巧,帮助开发者编写出更高效、更稳定的多线程应用程序。
|
8天前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第11天】 在Java中,高效的并发编程是提升应用性能和响应能力的关键。本文将探讨Java并发的核心概念,包括线程安全、锁机制、线程池以及并发集合等,同时提供实用的编程技巧和最佳实践,帮助开发者在保证线程安全的前提下,优化程序性能。我们将通过分析常见的并发问题,如竞态条件、死锁,以及如何利用现代Java并发工具来避免这些问题,从而构建更加健壮和高效的多线程应用程序。
|
1天前
|
安全 Java
java多线程(一)(火车售票)
java多线程(一)(火车售票)
|
1天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
1天前
|
安全 Java 程序员
Java中的多线程并发编程实践
【4月更文挑战第18天】在现代软件开发中,为了提高程序性能和响应速度,经常需要利用多线程技术来实现并发执行。本文将深入探讨Java语言中的多线程机制,包括线程的创建、启动、同步以及线程池的使用等关键技术点。我们将通过具体代码实例,分析多线程编程的优势与挑战,并提出一系列优化策略来确保多线程环境下的程序稳定性和性能。
|
2天前
|
缓存 分布式计算 监控
Java并发编程:深入理解线程池
【4月更文挑战第17天】在Java并发编程中,线程池是一种非常重要的技术,它可以有效地管理和控制线程的执行,提高系统的性能和稳定性。本文将深入探讨Java线程池的工作原理,使用方法以及在实际开发中的应用场景,帮助读者更好地理解和使用Java线程池。
|
2天前
|
存储 安全 Java
Java中的容器,线程安全和线程不安全
Java中的容器,线程安全和线程不安全
10 1
|
2天前
|
Java 开发者
Java中多线程并发控制的实现与优化
【4月更文挑战第17天】 在现代软件开发中,多线程编程已成为提升应用性能和响应能力的关键手段。特别是在Java语言中,由于其平台无关性和强大的运行时环境,多线程技术的应用尤为广泛。本文将深入探讨Java多线程的并发控制机制,包括基本的同步方法、死锁问题以及高级并发工具如java.util.concurrent包的使用。通过分析多线程环境下的竞态条件、资源争夺和线程协调问题,我们提出了一系列实现和优化策略,旨在帮助开发者构建更加健壮、高效的多线程应用。
3 0