Java守护线程概述

Java的线程分为两种:User Thread(用户线程)、DaemonThread(守护线程)。

只要当前JVM实例中尚存任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束是,守护线程随着JVM一同结束工作,Daemon作用是为其他线程提供便利服务,守护线程最典型的应用就是GC(垃圾回收器),他就是一个很称职的守护者。

User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

首先看一个例子,主线程中建立一个守护线程,当主线程结束时,守护线程也跟着结束。

package com.daemon;

import java.util.concurrent.TimeUnit;

public class DaemonThreadTest
{
	public static void main(String[] args)
	{
		Thread mainThread = new Thread(new Runnable(){
			@Override
			public void run()
			{
				Thread childThread = new Thread(new ClildThread());
				childThread.setDaemon(true);
				childThread.start();
				System.out.println("I'm main thread...");
			}
		});
		mainThread.start();
	}
}

class ClildThread implements Runnable
{
	@Override
	public void run()
	{
		while(true)
		{
			System.out.println("I'm child thread..");
			try
			{
				TimeUnit.MILLISECONDS.sleep(1000);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
}

运行结果:

I'm child thread..
I'm main thread...

如果不何止childThread为守护线程,当主线程结束时,childThread还在继续运行,如下:

package com.daemon;

import java.util.concurrent.TimeUnit;

public class DaemonThreadTest
{
	public static void main(String[] args)
	{
		Thread mainThread = new Thread(new Runnable(){
			@Override
			public void run()
			{
				Thread childThread = new Thread(new ClildThread());
				childThread.setDaemon(false);
				childThread.start();
				System.out.println("I'm main thread...");
			}
		});
		mainThread.start();
	}
}

class ClildThread implements Runnable
{
	@Override
	public void run()
	{
		while(true)
		{
			System.out.println("I'm child thread..");
			try
			{
				TimeUnit.MILLISECONDS.sleep(1000);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
}

运行结果:

I'm main thread...
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..(无限输出)

可以看到,当主线程结束时,childThread是非守护线程,就会无限的执行。

守护线程有一个应用场景,就是当主线程结束时,结束其余的子线程(守护线程)自动关闭,就免去了还要继续关闭子线程的麻烦。不过博主推荐,如果真有这种场景,还是用中断的方式实现比较合理。

还有补充一点,不是说当子线程是守护线程,主线程结束,子线程就跟着结束,这里的前提条件是:当前jvm应用实例中没有用户线程继续执行,如果有其他用户线程继续执行,那么后台线程不会中断,如下:

package com.daemon;

import java.util.concurrent.TimeUnit;

public class DaemonThreadTest
{
	public static void main(String[] args)
	{
		Thread mainThread = new Thread(new Runnable(){
			@Override
			public void run()
			{
				Thread childThread = new Thread(new ClildThread());
				childThread.setDaemon(true);
				childThread.start();
				System.out.println("I'm main thread...");
			}
		});
		mainThread.start();

		Thread otherThread = new Thread(new Runnable(){
			@Override
			public void run()
			{
				while(true)
				{
					System.out.println("I'm other user thread...");
					try
					{
						TimeUnit.MILLISECONDS.sleep(1000);
					}
					catch (InterruptedException e)
					{
						e.printStackTrace();
					}
				}
			}
		});
		otherThread.start();
	}
}

class ClildThread implements Runnable
{
	@Override
	public void run()
	{
		while(true)
		{
			System.out.println("I'm child thread..");
			try
			{
				TimeUnit.MILLISECONDS.sleep(1000);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
}

运行结果:

I'm other user thread...
I'm child thread..
I'm main thread...
I'm other user thread...
I'm child thread..
I'm other user thread...
I'm child thread..
I'm child thread..
I'm other user thread...
I'm other user thread...
I'm child thread..
I'm child thread..
I'm other user thread...
I'm other user thread...
I'm child thread..
I'm other user thread...
I'm child thread..
I'm other user thread...
I'm child thread..
I'm other user thread...
I'm child thread..
I'm other user thread...
I'm child thread..
I'm other user thread...
I'm child thread..
I'm other user thread...
I'm child thread..(无限输出)

如果需要在主线程结束时,将子线程结束掉,可以采用如下的中断方式:

package com.self;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadTest
{

	public static void main(String[] args)
	{
		Thread mainThread = new Thread(new Runnable(){
			public void run()
			{
				System.out.println("主线程开始...");
				Thread sonThread = new Thread(new Thread1(Thread.currentThread()));
				sonThread.setDaemon(false);
				sonThread.start();

				try
				{
					TimeUnit.MILLISECONDS.sleep(10000);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				System.out.println("主线程结束");
			}
		});
		mainThread.start();
	}

}

class Thread1 implements Runnable
{
	private Thread mainThread;

	public Thread1(Thread mainThread)
	{
		this.mainThread = mainThread;
	}

	@Override
	public void run()
	{
		while(mainThread.isAlive())
		{
			System.out.println("子线程运行中....");
			try
			{
				TimeUnit.MILLISECONDS.sleep(1000);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}

}

运行结果:

主线程开始...
子线程运行中....
子线程运行中....
子线程运行中....
子线程运行中....
子线程运行中....
子线程运行中....
子线程运行中....
子线程运行中....
子线程运行中....
子线程运行中....
子线程运行中....
主线程结束

回归正题,这里有几点需要注意:
(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。 
(3) 不要认为所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑。

写java多线程程序时,一般比较喜欢用java自带的多线程框架,比如ExecutorService,但是java的线程池会将守护线程转换为用户线程,所以如果要使用后台线程就不能用java的线程池。

如下,线程池中将daemon线程转换为用户线程的程序片段:

    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

注意到,这里不仅会将守护线程转变为用户线程,而且会把优先级转变为Thread.NORM_PRIORITY。

如下所示,将守护线程采用线程池的方式开启:

package com.daemon;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class DaemonThreadTest
{
	public static void main(String[] args)
	{
		Thread mainThread = new Thread(new Runnable(){
			@Override
			public void run()
			{
				ExecutorService exec = Executors.newCachedThreadPool();
				Thread childThread = new Thread(new ClildThread());
				childThread.setDaemon(true);
				exec.execute(childThread);
				exec.shutdown();
				System.out.println("I'm main thread...");
			}
		});
		mainThread.start();
	}
}

class ClildThread implements Runnable
{
	@Override
	public void run()
	{
		while(true)
		{
			System.out.println("I'm child thread..");
			try
			{
				TimeUnit.MILLISECONDS.sleep(1000);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
}

运行结果:

I'm main thread...
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..(无限输出)

上面代码证实了线程池会将守护线程转变为用户线程。

至于为什么jdk会这么做,博主还没有参悟的透彻,如果你了解,希望能留言告知。

时间: 2024-08-19 17:23:20

Java守护线程概述的相关文章

Java多线程:“基础篇”10之线程优先级和守护线程

1. 线程优先级的介绍 java 中的线程优先级的范围是1-10,默认的优先级是5."高优先级线程"会优先于 "低优先级线程"执行. java 中有两种线程:用户线程和守护线程.可以通过isDaemon()方法来区别它们:如果返回false, 则说明该线程是"用户线程":否则就是"守护线程". 用户线程一般用户执 行用户级任务,而守护线程也就是"后台线程",一般用来执行后台任务.需要注意的是: Java虚拟

Java线程:线程的调度-守护线程

守护线程使用的情况较少,但并非无用,举例来说,JVM的垃圾回收.内存管理等线程都是守护线程.还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数.超时时间.状态等等. setDaemon方法的详细说明: public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程.当正在运行的线程都是守护线程时,Java 虚拟机退出. 该方法必须在启动线程前调用. 该方法首先调用该线程的 checkAccess 方法,且不

java并发编程学习: 守护线程(Daemon Thread)

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

Java并发编程示例(七):守护线程的创建和运行_java

Java有一种特殊线程,守护线程,这种线程优先级特别低,只有在同一程序中的其他线程不执行时才会执行. 由于守护线程拥有这些特性,所以,一般用为为程序中的普通线程(也称为用户线程)提供服务.它们一般会有一个无限循环,或用于等待请求服务,或用于执行任务等.它们不可以做任何重要的工作,因为我们不确定他们什么时才能分配到CPU运行时间,而且当没有其他线程执行时,它们就会自动终止.这类线程的一个典型应用就是Java的垃圾回收. 在本节示例中,我们将创建两个线程,一个是普通线程,向队列中写入事件:另外一个是

Java线程之守护线程(Daemon)用法实例_java

本文实例讲述了Java线程之守护线程(Daemon)用法.分享给大家供大家参考.具体如下: 守护线程(Daemon) Java有两种Thread:"守护线程Daemon"与"用户线程User". 我们之前看到的例子都是用户,守护线程是一种"在后台提供通用性支持"的线程,它并不属于程序本体. 从字面上我们很容易将守护线程理解成是由虚拟机(virtual machine)在内部创建的,而用户线程则是自己所创建的.事实并不是这样,任何线程都可以是&qu

Java并发编程:守护线程

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

《Java多线程编程核心技术》——1.11节守护线程

1.11 守护线程在Java线程中有两种线程,一种是用户线程,另一种是守护线程.守护线程是一种特殊的线程,它的特性有"陪伴"的含义,当进程中不存在非守护线程了,则守护线程自动销毁.典型的守护线程就是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁.用个比较通俗的比喻来解释一下"守护线程":任何一个守护线程都是整个JVM中所有非守护线程的"保姆",只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工

JAVA之旅(十五)——多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止

JAVA之旅(十五)--多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止 我们接着多线程讲 一.生产者和消费者 什么是生产者和消费者?我们解释过来应该是生产一个,消费一个,的意思,具体我们通过例子来说 package com.lgl.hellojava; //公共的 类 类名 public class HelloJJAVA { public static void main(String[] args) { /** * 生产者和消费者

深入JVM剖析Java的线程堆栈_java

在这篇文章里我将教会你如何分析JVM的线程堆栈以及如何从堆栈信息中找出问题的根因.在我看来线程堆栈分析技术是Java EE产品支持工程师所必须掌握的一门技术.在线程堆栈中存储的信息,通常远超出你的想象,我们可以在工作中善加利用这些信息. 我的目标是分享我过去十几年来在线程分析中积累的知识和经验.这些知识和经验是在各种版本的JVM以及各厂商的JVM供应商的深入分析中获得的,在这个过程中我也总结出大量的通用问题模板. 那么,准备好了么,现在就把这篇文章加入书签,在后续几周中我会给大家带来这一系列的专