一、后台线程(守护线程)

  学一个东西,最重要的一点就是,为什么要用它?

  后台线程区别于普通线程,普通线程又可以称为用户线程,只完成用户自己想要完成的任务,不提供公共服务。而有时,我们希望编写一段程序,能够提供公共的服务,保证所有用户针对该线程的请求都能有响应。

  仔细来看下后台线程的定义:指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。

二、实现后台线程

  1.我们先定义任务及响应的线程

  定义任务:Thread.yield();让线程暂停一段时间

class DaemonSpawn implements Runnable{
public void run(){
while(true)
Thread.yield();
}
}

  定一个线程,有一个属性Thread[] t,用于存放子线程

class Daemon implements Runnable{
//该任务下创建很多子线程
private Thread[] t = new Thread[10];
public void run(){
//为线程池填充线程,并将所有线程启动
for(int i = 0 ; i < t.length ; i++){
t[i] = new Thread(new DaemonSpawn());
t[i].start();
System.out.println("DaemonSpawn "+i+"started, ");
}
for(int i = 0 ; i < t.length ; i++){
System.out.println("t["+i+"].isDaemon()="+t[i].isDaemon()+", ");
}
/*
* Daemon进入了无线循环,并在循环里调用yield方法把控制权交给其他进程
*/
while(true)
Thread.yield();
}
}

  讲定义的线程设定为后台线程

  public static void main(String[] args) throws InterruptedException{
/*
* Daemon被设置为了后台线程,它的所有子线程也自然就是后台线程了
*/
Thread d = new Thread(new Daemon());
d.setDaemon(true);
d.start();
System.out.println("d.isDaemon()="+d.isDaemon()+",");
TimeUnit.SECONDS.sleep(1);
}

  至此,后台线程已定义并跑起来了。输出结果:

DaemonSpawn 0started,
DaemonSpawn 1started,
DaemonSpawn 2started,
DaemonSpawn 3started,
DaemonSpawn 4started,
DaemonSpawn 5started,
DaemonSpawn 6started,
DaemonSpawn 7started,
DaemonSpawn 8started,
DaemonSpawn 9started,
t[0].isDaemon()=true,
t[1].isDaemon()=true,
t[2].isDaemon()=true,
t[3].isDaemon()=true,
t[4].isDaemon()=true,
t[5].isDaemon()=true,
t[6].isDaemon()=true,
t[7].isDaemon()=true,
t[8].isDaemon()=true,
t[9].isDaemon()=true,
d.isDaemon()+true,

  2.有一点要指出:所有的“非后台线程”结束时,程序也就终止了,同时会杀死进程中所有后台线程:main就是一个非后台线程

   首先,如何证明main是非后台线程,还是用是上面那段程序

    其次,如何证明非后台线程退出后,后台线程会被杀死呢?

public class TaskDaemon implements Runnable{

    @Override
public void run() {
try{
while(true){
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread()+" "+this);
}
}catch(InterruptedException e){
System.out.println("sleep() interrupted");
}
}
public static void main(String[] args) throws InterruptedException{
/*
* 可以通过查看该程序的结果理解后台线程
* 创建了9个线程,都声明为后台线程,然后启动他们,在非后台线程结束之前,后台线程会被线程调用器调用
* main就是一个非后台线程,for循环结束之后输出了"All daemons started"证明main快要结束了,但是你让它睡眠了一会保证main不退出
* 这样后台线程就会跑着,于是有了后面的打印结果
*/
for(int i = 0 ; i < 10 ; i++){
//后台线程本质上也是一个线程,通过任务来创建该线程
Thread daemon = new Thread(new TaskDaemon());
//想将创建的线程声明为后台线程 ,必须在启动前将其设置为true
daemon.setDaemon(true);
daemon.start();
}
System.out.println("All daemons started");
TimeUnit.MILLISECONDS.sleep(175);
} }

  3.通过isDaemon()方法来判断一个线程是否是一个后台线程

   一个后台线程创建的任何线程都将被自动设置成后台线程,例如:Daemons中所示。

  4.后台线程在不执行finally字句的情况下就会终止其run()方法,例如:DaemonsDontRunFinally

class ADaemon implements Runnable{
@Override
public void run() {
try{
System.out.println("Starting ADaemon");
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e){
System.out.println("Exiting via InterruptedException");
}finally{
System.out.println("Thie should always run?");
}
}
}
public static void main(String[] args){
//当最后一个非后台线程终止时,后台线程会“突然”终止
//故一旦main退出,jvm就会立即关闭所有的后台进程,而不会有任何你希望出现的确认形式
Thread t = new Thread(new ADaemon());
//如果注释掉下面这句话,finally将会执行
t.setDaemon(true);
t.start();
}

  可以看到输出结果,finally中结果并没有执行

三、自定义后台线程工厂

  1.自定义后台线程工厂

public class TaskDaemonFactory implements ThreadFactory{
public Thread newThread(Runnable r){
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
}

  2.创建线程时试用该工厂

     /*
* Executors.newCachedThreadPool();方法用来接受一个ThreadFactory对象,而这个对象将被用来创建新的线程
* 所以,你的Facotry重写了ThreadFacotry方法之后,要去实现他的创建线程方法,方法里默认将线程声明为后台线程
*/
ExecutorService exec = Executors.newCachedThreadPool(new TaskDaemonFactory());
for(int i = 0 ;i < 10 ;i++){
exec.execute(new TaskDaemonFromFactory());//这个是一个自定义任务
}
System.out.println("All daemons started");
TimeUnit.MILLISECONDS.sleep(500);

四、总结

后台线程(daemon)
  |——定义:指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分
  |       |——所有的“非后台线程”结束时,程序也就终止了,同时会杀死进程中所有后台线程:main就是一个非后台线程
  |——声明并试用后台线程
  |       |——传统方式:通过声明线程,操作线程来定义后台线程
  |       |         |——Thread daemon = new Thread(new TaskDaemon());//将任务交给线程也叫声明线程
  |       |         |—— daemon.setDaemon(true);//将线程设置为后台线程
  |       |         |——daemon.start();//启动后台线程
  |       |——由executor调用线程工厂:通过编写定制的ThreadFactory,可以定制由Executor创建的线程的属性
  |           |——1.实现ThreadFactory接口
  |           |——2.重写newThread方法
  |           |—— public Thread newThread(Runnable r){
  |           |—— Thread t = new Thread(r);
  |           |—— t.setDaemon(true);
  |           |—— return t;
  |           |—— }
  |           |——3.将定制的TaskDaemonFactory传递给Executor,它将用此来生成对应的线程,并操纵他们
  |           |—— 每个静态的ExecutorService创建方法都被重载为接受一个ThreadFactory对象,该对象将被用来创建新的线程
  |           |—— ExecutorService exec = Executors.newCachedThreadPool(new TaskDaemonFactory());
  |           |——4.将任务传递给exec,它会帮你执行线程操作
  |           |—— exec.execute(new TaskDaemonFromFactory());

注:以上代码均来自《Thinking in java》,总结内容均是个人理解,如有错误请大家批评指正,谢谢

Java多线程——<五>后台线程(daemon)的更多相关文章

  1. Java多线程之后台线程不执行finally

    后台线程不执行finally package wzh.daemon; import java.util.concurrent.TimeUnit; class ADaemon implements Ru ...

  2. Java多线程之后台线程

    将线程设置成后台线程Daemons 主线程结果后,后台线程将自动结果. package wzh.test; import java.util.concurrent.TimeUnit; class Si ...

  3. Java多线程之守护线程

    Java多线程之守护线程 一.前言 Java线程有两类: 用户线程:运行在前台,执行具体的任务,程序的主线程,连接网络的子线程等都是用户线程 守护线程:运行在后台,为其他前台线程服务 特点:一旦所有用 ...

  4. Java多线程并发02——线程的生命周期与常用方法,你都掌握了吗

    在上一章,为大家介绍了线程的一些基础知识,线程的创建与终止.本期将为各位带来线程的生命周期与常用方法.关注我的公众号「Java面典」了解更多 Java 相关知识点. 线程生命周期 一个线程不是被创建了 ...

  5. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  6. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  7. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  8. Java多线程系列--“JUC线程池”02之 线程池原理(一)

    概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...

  9. Java多线程系列--“JUC线程池”04之 线程池原理(三)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...

随机推荐

  1. Sql中判断"库、表、列,视图,存储过程"是否存在

    --判断数据库是否存在 IF EXISTS (SELECT * FROM MASTER.sys.sysdatabases WHERE NAME = '库名') PRINT 'exists ' else ...

  2. .net上传图片并转成二进制流

    话不多说,直接上代码 <input id="InputFile" style="width: 399px" type="file" r ...

  3. iOS - 苹果健康架构 & 基于HealthKit的健康数据的编辑

    最近公司需求,研究了一周之久的苹果健康架构,内容包括:资料调研.报告与HealthKit.framework - API,这一研习还在持续进行中.至此,主要认识到了2点:对苹果健康健康架构设计与实现原 ...

  4. iOS中-Qutarz2D详解及使用

    在iOS中Qutarz2D 详解及使用 (一)初识 介绍 Quartz 2D是二维绘图引擎. 能完成的工作有: 绘制图形 : 线条\三角形\矩形\圆\弧等 绘制文字 绘制\生成图片(图像) 读取\生成 ...

  5. 20150222—LINQ to SQL 插入、更新和删除

    注意,使用LINQ to SQL时, 表中必须有一个主键才可以起效,否则系统将无法对数据作出修改 插入新数据,根据上一片的文章实例在其中添加新的控件: 编号TextBox(Name):sno 名字Te ...

  6. WCF之错误和异常

    CLR异常无法跨越服务边界,所有的异常都被封装(序列化)为SOAP Fault,可以让所有平台的用户接收到. SOAP1.1只有Body.1.2中含有Header+Body. 未捕获异常 异常会从逻辑 ...

  7. Codevs 2627 村村通

    时间限制: 1 s   空间限制: 32000 KB  题目等级 : 黄金 Gold  题目描述 Description 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到 ...

  8. 使用awstats分析iis站点的日志

    环境:win7 + iis7 + perl(ActivePerl-5.20.1.2000) + awstats 7.3 一.找到iis日志所在目录 建议全部都打勾 二.安装perl AWStats是p ...

  9. [大牛翻译系列]Hadoop(22)附录D.2 复制连接框架

    附录D.2 复制连接框架 复制连接是map端连接,得名于它的具体实现:连接中最小的数据集将会被复制到所有的map主机节点.复制连接的实现非常直接明了.更具体的内容可以参考Chunk Lam的<H ...

  10. GoldenGate单向复制配置示例

    一:环境介绍 --source端 ip地址:192.168.123.10 数据库版本:11.2.0.1.0 32 bit 操作系统版本:centos 4.5 32 bit ogg版本:fbo_ggs_ ...