美文网首页multiThread线程
Java 守护线程Daemon

Java 守护线程Daemon

作者: Alex_1799 | 来源:发表于2018-06-26 15:38 被阅读43次

    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();  
                }  
            }  
        }  
    }
    
    image.png

    如果不何止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();  
             }  
         }  
     }  
    }
    

    运行结果:


    image.png

    可以看到,当主线程结束时,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();  
                }  
            }  
        }  
    }
    
    image.png
    如果需要在主线程结束时,将子线程结束掉,可以采用如下的中断方式:
    
    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();  
                }  
            }  
        }  
           
    }
    
    image.png

    回归正题,这里有几点需要注意:
    (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();  
                }  
            }  
        }  
    }
    
    image.png

    相关文章

      网友评论

        本文标题:Java 守护线程Daemon

        本文链接:https://www.haomeiwen.com/subject/rgqmyftx.html