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

  • 相关阅读:
    测试报告M2
    11.24Daily Scrum(4)
    11.24Daily Scrum(3)
    11.24Daily Scrum(2)
    11.24Daily Scrum
    11.22Daily Scrum(2)
    11.22Daily Scrum
    Echarts中graph类型的运用求教
    Echarts学习求教
    用node编写自己的cli工具
  • 原文地址:https://www.cnblogs.com/aspirant/p/8628601.html
Copyright © 2020-2023  润新知