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

时间: 2024-10-01 00:23:58

java并发编程学习: 守护线程(Daemon Thread)的相关文章

Java并发编程:守护线程

在Java中有两类线程:用户线程(UserThread).守护线程(DaemonThread). 所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分.因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程.反过来说,只要任何非守护线程还在运行,程序就不会终止. 用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在

Java 并发编程学习笔记之核心理论基础_java

并发编程是Java程序员最重要的技能之一,也是最难掌握的一种技能.它要求编程者对计算机最底层的运作原理有深刻的理解,同时要求编程者逻辑清晰.思维缜密,这样才能写出高效.安全.可靠的多线程并发程序.本系列会从线程间协调的方式(wait.notify.notifyAll).Synchronized及Volatile的本质入手,详细解释JDK为我们提供的每种并发工具和底层实现机制.在此基础上,我们会进一步分析java.util.concurrent包的工具类,包括其使用方式.实现源码及其背后的原理.本

Java 并发编程学习笔记之Synchronized简介_java

一.Synchronized的基本使用 Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法.Synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题.从语法上讲,Synchronized总共有三种用法: (1)修饰普通方法 (2)修饰静态方法 (3)修饰代码块 接下来我就通过几个例子程序来说明一下这三种使用方式(为了便于比较,三段代码除了Synchronized的使用方式不同以外,

Java并发编程之创建线程_java

先讲述一下Java中的应用程序和进程相关的概念知识,然后再阐述如何创建线程以及如何创建进程.下面是本文的目录大纲: 一.Java中关于应用程序和进程相关的概念 二.Java中如何创建线程 三.Java中如何创建进程 一.Java中关于应用程序和进程相关的概念 在Java中,一个应用程序对应着一个JVM实例(也有地方称为JVM进程),一般来说名字默认为java.exe或者javaw.exe(windows下可以通过任务管理器查看).Java采用的是单线程编程模型,即在我们自己的程序中如果没有主动创

java并发编程学习:如何等待多个线程执行完成后再继续后续处理(synchronized、join、FutureTask、CyclicBarrier)

多线程应用中,经常会遇到这种场景:后面的处理,依赖前面的N个线程的处理结果,必须等前面的线程执行完毕后,后面的代码才允许执行. 在我不知道CyclicBarrier之前,最容易想到的就是放置一个公用的static变量,假如有10个线程,每个线程处理完上去累加下结果,然后后面用一个死循环(或类似线程阻塞的方法),去数这个结果,达到10个,说明大家都爽完了,可以进行后续的事情了,这个想法虽然土鳖,但是基本上跟语言无关,几乎所有主流编程语言都支持. package yjmyzz.test; publi

java并发编程学习:用 Semaphore (信号量)控制并发资源

并发编程这方面以前关注得比较少,恶补一下,推荐一个好的网站:并发编程网 - ifeve.com,上面全是各种大牛原创或编译的并发编程文章. 今天先来学习Semaphore(信号量),字面上看,根本不知道这东西是干啥的,借用 并发工具类(三)控制并发线程数的Semaphore一文中的交通红绿信号灯的例子来理解一下: 一条4车道的主干道,假设100米长,每辆车假设占用的长度为10米(考虑到前后车距),也就是说这条道上满负载运行的话,最多只能容纳4*(100/10)=40辆车,如果有120辆车要通过的

java并发编程学习: ThreadLocal使用及原理

多线程应用中,如果希望一个变量隔离在某个线程内,即:该变量只能由某个线程本身可见,其它线程无法访问,那么ThreadLocal可以很方便的帮你做到这一点.  先来看一下示例: package yjmyzz.test; public class ThreadLocalTest1 { public static class MyRunnable implements Runnable { private ThreadLocal<Integer> threadLocal = new ThreadLo

JAVA并发编程学习笔记之CAS操作

CAS操作 CAS是单词compare and set的缩写,意思是指在set之前先比较该值有没有变化,只有在没变的情况下才对其赋值. 我们常常做这样的操作 if(a==b) { a++; } 试想一下如果在做a++之前a的值被改变了怎么办?a++还执行吗?出现该问题的原因是在多线程环境下,a的值处于一种不定的状态.采用锁可以解决此类问题,但CAS也可以解决,而且可以不加锁. int expect = a; if(a.compareAndSet(expect,a+1)) { doSomeThin

java并发编程学习: 原子变量(CAS)

先上一段代码: package test; public class Program { public static int i = 0; private static class Next extends Thread { public void run() { i = i + 1; System.out.println(i); } } public static void main(String[] args) { Thread[] threads = new Thread[10]; for