Java多线程——<五>后台线程(daemon)

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

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

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

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

二、实现后台线程

  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》,总结内容均是个人理解,如有错误请大家批评指正,谢谢

时间: 2024-10-12 12:10:47

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

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

后台线程不执行finally package wzh.daemon; import java.util.concurrent.TimeUnit; class ADaemon implements Runnable { @Override public void run() { try { System.out.println("Starting ADaemon"); TimeUnit.SECONDS.sleep(1); } catch (Exception e) { System.ou

Java多线程之后台线程

将线程设置成后台线程Daemons 主线程结果后,后台线程将自动结果. package wzh.test; import java.util.concurrent.TimeUnit; class SimpleDaemons implements Runnable{ @Override public void run() { try { while (true) { TimeUnit.MILLISECONDS.sleep(100); System.out.println(Thread.curren

Java多线程-新特性-线程池

Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定可靠的多线程程序,线程部分的新增内容显得尤为重要. 有关Java5线程新特征的内容全部在java.util.concurrent下面,里面包含数目众多的接口和类,熟悉这部分API特征是一项艰难的学习过程.目前有关这方面的资料和书籍都少之又少,大部分介绍线程方面书籍还停留在java5之前的知识层面上. 在Java5之

Java多线程(五) Lock接口,ReentranctLock,ReentrantReadWriteLock

在JDK5里面,提供了一个Lock接口.该接口通过底层框架的形式为设计更面向对象.可更加细粒度控制线程代码.更灵活控制线程通信提供了基础.实现Lock接口且使用得比较多的是可重入锁(ReentrantLock)以及读写锁(ReentrantReadWriteLock). 1. ReentrantLock 在Java多线程(二) 多线程的锁机制 里面,已经总结过通过使用Synchronized关键字实现线程内的方法锁定.但使用Synchronized关键字有一些局限性,上锁和释放锁是由JVM决定的

JAVA并发编程——守护线程(Daemon Thread)

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

Java多线程总结之线程安全队列Queue

在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列.Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列. 注:什么叫线程安全?这个首先要明确.线程安全的类 ,指的是类内共享的全局变量的访问必须保证是不受多线程形式影响的.如果由于多线程的访问(比如修改.遍历.查看)而使这些变量结构被破坏

JAVA并发,后台线程

1 package com.xt.thinks21_2; 2 3 import java.util.concurrent.TimeUnit; 4 5 /** 6 * 后台线程测试 7 * 8 * @author Administrator 9 * 10 */ 11 public class SimpleDaemonTest implements Runnable { 12 13 @Override 14 public void run() { 15 // TODO Auto-generated

Java多线程系列--“JUC线程池”01之 线程池架构

概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容——线程池.内容包括:线程池架构图线程池示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509903.html 线程池架构图 线程池的架构图如下: 1. Executor 它是"执行者"接口,它是来执行任务的.准确的说,Executor提供了execute()接口来执行

关于java多线程中守护线程的理解

在java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) 守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程.下面的方法就是用来设置守护线程的. 1 Thread daemonTread = new Thread(); 2 3 // 设定 daemonThread 为 守护线程,default false(非守护线程) 4 daemonThread.setDaemon(true); 5 6 // 验证当前线程是否为守护线程,返回 tr