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…
- 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..(无限输出)
守护线程有一个应用场景,就是当主线程结束时,结束其余的子线程(守护线程)自动关闭,就免去了还要继续关闭子线程的麻烦。不过博主推荐,如果真有这种场景,还是用中断的方式实现比较合理。
还有补充一点,不是说当子线程是守护线程,主线程结束,子线程就跟着结束,这里的前提条件是:当前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;
- }
- }
如下所示,将守护线程采用线程池的方式开启:
- 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..(无限输出)
至于为什么jdk会这么做,博主还没有参悟的透彻,如果你了解,希望能留言告知。
转载:https://blog.csdn.net/u013256816/article/details/50392298