原文出处: 朱小厮

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..(无限输出)

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

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

  1. Java守护线程

    最近的项目使用的是dubbo.Web工程发布在Tomcat上,会作为消费者调用其他的dubbo微服务.但是最近发现一个问题,在使用shutdown命令关闭tomcat的时候,Tomcat并没有真正关闭 ...

  2. 白话JAVA守护线程

    OneCoder(苦逼Coder)原创,转载请务必注明出处: http://www.coderli.com/archives/daemon-thread-plain-words/ 关于“白话”:偶然想 ...

  3. 转:JAVA守护线程

    原文地址:https://www.cnblogs.com/wxgblogs/p/5417503.html 详细内容看原文~  ,写的挺好的 在Java中有两类线程:User Thread(用户线程). ...

  4. JAVA - 守护线程(Daemon Thread)

    转载自:http://www.cnblogs.com/luochengor/archive/2011/08/11/2134818.html 在Java中有两类线程:用户线程 (User Thread) ...

  5. java守护线程。

    java的守护线程:具体定义我也不太清楚,百度和谷歌了看的也不是很明白,但是啊,下边有给出一个例子自己领悟吧. 一.计时器的Timer声明时是否声明为守护线程对计时器的影响. /** * */ pac ...

  6. java 守护线程整理

    java中finally语句不走的可能存在system.exit(0)与守护线程 线程sleep采用TimeUnit类 设定线程的名字thread.getcurrentThread().setName ...

  7. Java 守护线程(Daemon) 例子

    当我们在Java中创建一个线程,缺省状态下它是一个User线程,如果该线程运行,JVM不会终结该程序.当一个线被标记为守护线程,JVM不会等待其结束,只要所有用户(User)线程都结束,JVM将终结程 ...

  8. 从 JVM 视角看看 Java 守护线程

    Java 多线程系列第 7 篇. 这篇我们来讲讲线程的另一个特性:守护线程 or 用户线程? 我们先来看看 Thread.setDaemon() 方法的注释,如下所示. Marks this thre ...

  9. java守护线程的理解

    package daemonThread; /*setDaemon(true)方法将线程设置为守护线程,线程的Daemon默认值为false * 只要当前JVM实例中存在任何一个非守护线程没有结束,守 ...

随机推荐

  1. crontab任务不生效

    新建php脚本ctTest.php,代码如下: <?php /****************************************************************** ...

  2. css3学习笔记一

    首先界面是二维的但也可以有三维的效果.先了解浏览器兼容性问题,火狐加前缀(-moz-)IE加(-MF-)谷歌加(-webkit),简单介绍css3的几个属性. 对于背景来说如果是单纯着一种颜色可以会单 ...

  3. Redis4.0模块子系统实现简述

    一.模块加载方法 1.在配置文件或者启动参数里面通过<loadmodule /path/to/mymodule.so args>指令加载 2.Redis启动后,通过<module l ...

  4. DTCping 的简单使用与排错

    1. 工具下载路径 https://support.microsoft.com/zh-cn/help/918331/how-to-troubleshoot-connectivity-issues-in ...

  5. 多校联赛7 1001 hdu 4666(最远哈曼顿距离+优先队列)

    吐个糟,尼玛今天被虐成狗了,一题都没搞出来,这题搞了N久居然还是搞不出来,一直TLE,最后还是参考别人代码才领悟的,思路就这么简单, 就是不会转弯,看着模板却不会改,艹,真怀疑自己是不是个笨蛋题意:求 ...

  6. delphi(假三层之数据访问层)(第一天)

    本论文主要是通过三天来讲解三层的结构,今天是第一天,先讲解一下delphi下的Models层,我主要封装了两个查询得到数据集的函数,主要是通过在表示层上创建的数数据集控件传递进来,通过业务逻辑对语句的 ...

  7. 深入理解JAVA虚拟机阅读笔记6——线程安全与锁优化

    线程安全:如果一个对象可以安全的被多个线程同时使用,那它就是线程安全的. 一.Java中的线程安全 1.不可变 不可变的对象一定是线程安全的.String.枚举类型.java.lang.Number的 ...

  8. 深入理解JAVA虚拟机阅读笔记2——垃圾回收

    线程私有的程序计数器.虚拟机栈和本地方法栈随线程而生,随线程而灭.栈中的栈帧随方法的进入和退出有条不紊的入栈和出栈. 而Java堆和方法区因为需要多大内存.创建多少对象都是不确定的,因此这两个区域是垃 ...

  9. Angular 动态组件

    Angular 动态组件 实现步骤 Directive HostComponent 动态组件 AdService 配置AppModule 需要了解的概念 Directive 我们需要一个Directi ...

  10. 更新ffmpeg

    今天对公司线上的几台机器做了下ffmpeg的更新,没有什么技术含量,还是简单记录下,做个流水账~哈哈 软件包获取方式 官方网站:https://ffmpeg.org/download.htmlgith ...