Java中后台线程详细解答
以前从来没有听说过,java中有后台线程这种东西。一般来说,JVM(JAVA虚拟机)中一般会包括俩种线程,分别是用户线程和后台线程。所谓后台线程(daemon)线程指的是:在程序运行的时候在后台提供的一种通用的服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束的时候,也就是用户线程都结束的时候,程序也就终止了。同时,会杀死进程中的所有的后台线程。反过来说,只要有任何
·
以前从来没有听说过,java中有后台线程这种东西。一般来说,JVM(JAVA虚拟机)中一般会包括俩种线程,分别是用户线程和后台线程。所谓后台线程(daemon)线程指的是:在程序运行的时候在后台提供的一种通用的服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束的时候,也就是用户线程都结束的时候,程序也就终止了。同时,会杀死进程中的所有的后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会结束。不如执行main()的就是一个非后台线程。
基于这个特点,当虚拟机中的用户线程全部退出运行时,守护线程没有服务的对象后,JVM也就退出了。
这点JDK源码中的介绍已经说明这一点了。
- * Marks this thread as either a {@linkplain #isDaemon daemon} thread
- * or a user thread. The Java Virtual Machine exits when the only
- * threads running are all daemon threads.
1.后台线程的启动的条件:
/*必须在启动线程之前调用SetDaemon()方法,才能把这个线程设置为后台线程。 * 在这个程序里面,当我们输入了字符串以后,那么Main线程就会停止运行了 * 那么程序中已经没有可以运行的用户的线程了。所以后台线程就会被停止了 * JVM也就会被停停止了,感兴趣的读者可以自己尝试一下*/ public class DaemonRunner implements Runnable { @Override public void run() { while (true) { for (int i = 0; i < 3; i++) { System.out.println("守护线程" + i); } } } public static void main(String[] args) { Thread daemon = new Thread(new DaemonRunner()); daemon.setDaemon(true); daemon.start(); Scanner s = new Scanner(System.in); String string=s.nextLine(); Runtime.getRuntime().addShutdownHook(new Thread(){ @Override public void run() { super.run(); System.out.println("JVM退出"); try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } }); } }
2.在后台线程中启动的线程都属于后台线程。尽管你没有明确指明它们是后台线程,但是它们的确是后台线程。
/*可以通过调用isDaemon()方法来确定线程是否是一个后台线程。如果是一个后台线程, * 那么它创建的任何线程都被自动设置成后台的线程 * 在这个实例中,Daemon线程被设置成了后台模式,然后派生出许多子线程,这些线程并没有被设置成 * 后台模式,不过它们的确是后台线程。接着,Daemon线程进入了无限循环,并且在循环里调用了yield方法 * 把控制权交给其它的线程或者进程*/ class Daemon implements Runnable{ private Thread[] t = new Thread[10]; @Override 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()); } while (true) { Thread.yield(); } } } class DaemonSpawn implements Runnable{ @Override public void run() { while (true) { Thread.yield(); } } } public class Daemons { public static void main(String[] args) { Thread d = new Thread(new Daemon()); d.setDaemon(true); d.start(); System.out.println("d.isDaemon()=" + d.isDaemon()); try { TimeUnit.SECONDS.sleep(1);//让启动的后台的线程可以获得一定的执行的时间。 } catch (InterruptedException e) { e.printStackTrace(); } } }最后的执行的结果如下:
d.isDaemon()=true
DaemonSpawn 0started
DaemonSpawn 1started
DaemonSpawn 2started
DaemonSpawn 3started
DaemonSpawn 4started
DaemonSpawn 5started
DaemonSpawn 6started
DaemonSpawn 7started
DaemonSpawn 8started
DaemonSpawn 9started
t[0].isDaemontrue
t[1].isDaemontrue
t[2].isDaemontrue
t[3].isDaemontrue
t[4].isDaemontrue
t[5].isDaemontrue
t[6].isDaemontrue
t[7].isDaemontrue
t[8].isDaemontrue
t[9].isDaemontrue
DaemonSpawn 0started
DaemonSpawn 1started
DaemonSpawn 2started
DaemonSpawn 3started
DaemonSpawn 4started
DaemonSpawn 5started
DaemonSpawn 6started
DaemonSpawn 7started
DaemonSpawn 8started
DaemonSpawn 9started
t[0].isDaemontrue
t[1].isDaemontrue
t[2].isDaemontrue
t[3].isDaemontrue
t[4].isDaemontrue
t[5].isDaemontrue
t[6].isDaemontrue
t[7].isDaemontrue
t[8].isDaemontrue
t[9].isDaemontrue
3.通过为Executors.newCachedThreadPool()方法指定一个ThreadFactory的对象。通过这种方法,我们也可以将
我们想要启动的线程设置为后台线程。
/*在这个例子中,对于这个静态的构造方法:Executors.newCachedThreadPool(new DaemonThreadFactory() * 我们可以为传入一个ThreadFactory的对象,那么我们就可以通过这种方法,将我们想要启动的线程设置为后台线程 * 这是要注意的。*/ class DaemonThreadFactory implements ThreadFactory{ @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t; } } /*在这个例子中,在Main方法中,会首先调用Main方法中的普通的方法,“ System.out.println("All dameons started");” * 所以会首先打印这一条语句。然后在主线程休眠期间,相应的后台线程,就会获得执行的时间,最后在Main线程 * 结束了运行的时候,也就是当Main线程从休眠中恢复过来的时候,那么Main线性就会结束运行。接着, * 那么所有的后台的线程都会停止。JVM也会停止执行。*/ public class DaemonFromFactory 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("Interrupted"); } } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(new DaemonThreadFactory()); for (int i = 0; i < 10; i++) { exec.execute(new DaemonFromFactory()); } System.out.println("All dameons started"); try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }
最后的输出的结果为:
All dameons started
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-0,5,main] Concurrency.DaemonFromFactory@144fe080
Thread[Thread-1,5,main] Concurrency.DaemonFromFactory@104fa29e
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
Thread[Thread-9,5,main] Concurrency.DaemonFromFactory@1a7288d1
Thread[Thread-7,5,main] Concurrency.DaemonFromFactory@25144c3e
Thread[Thread-4,5,main] Concurrency.DaemonFromFactory@288523d
Thread[Thread-6,5,main] Concurrency.DaemonFromFactory@1edae2a8
Thread[Thread-5,5,main] Concurrency.DaemonFromFactory@626007aa
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-6,5,main] Concurrency.DaemonFromFactory@1edae2a8
Thread[Thread-5,5,main] Concurrency.DaemonFromFactory@626007aa
Thread[Thread-4,5,main] Concurrency.DaemonFromFactory@288523d
Thread[Thread-9,5,main] Concurrency.DaemonFromFactory@1a7288d1
Thread[Thread-7,5,main] Concurrency.DaemonFromFactory@25144c3e
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
Thread[Thread-1,5,main] Concurrency.DaemonFromFactory@104fa29e
Thread[Thread-0,5,main] Concurrency.DaemonFromFactory@144fe080
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-6,5,main] Concurrency.DaemonFromFactory@1edae2a8
Thread[Thread-1,5,main] Concurrency.DaemonFromFactory@104fa29e
Thread[Thread-0,5,main] Concurrency.DaemonFromFactory@144fe080
Thread[Thread-7,5,main] Concurrency.DaemonFromFactory@25144c3e
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
Thread[Thread-5,5,main] Concurrency.DaemonFromFactory@626007aa
Thread[Thread-9,5,main] Concurrency.DaemonFromFactory@1a7288d1
Thread[Thread-4,5,main] Concurrency.DaemonFromFactory@288523d
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
Thread[Thread-7,5,main] Concurrency.DaemonFromFactory@25144c3e
Thread[Thread-4,5,main] Concurrency.DaemonFromFactory@288523d
Thread[Thread-6,5,main] Concurrency.DaemonFromFactory@1edae2a8
Thread[Thread-1,5,main] Concurrency.DaemonFromFactory@104fa29e
Thread[Thread-0,5,main] Concurrency.DaemonFromFactory@144fe080
Thread[Thread-9,5,main] Concurrency.DaemonFromFactory@1a7288d1
Thread[Thread-5,5,main] Concurrency.DaemonFromFactory@626007aa
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-0,5,main] Concurrency.DaemonFromFactory@144fe080
Thread[Thread-1,5,main] Concurrency.DaemonFromFactory@104fa29e
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
Thread[Thread-9,5,main] Concurrency.DaemonFromFactory@1a7288d1
Thread[Thread-7,5,main] Concurrency.DaemonFromFactory@25144c3e
Thread[Thread-4,5,main] Concurrency.DaemonFromFactory@288523d
Thread[Thread-6,5,main] Concurrency.DaemonFromFactory@1edae2a8
Thread[Thread-5,5,main] Concurrency.DaemonFromFactory@626007aa
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-6,5,main] Concurrency.DaemonFromFactory@1edae2a8
Thread[Thread-5,5,main] Concurrency.DaemonFromFactory@626007aa
Thread[Thread-4,5,main] Concurrency.DaemonFromFactory@288523d
Thread[Thread-9,5,main] Concurrency.DaemonFromFactory@1a7288d1
Thread[Thread-7,5,main] Concurrency.DaemonFromFactory@25144c3e
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
Thread[Thread-1,5,main] Concurrency.DaemonFromFactory@104fa29e
Thread[Thread-0,5,main] Concurrency.DaemonFromFactory@144fe080
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-6,5,main] Concurrency.DaemonFromFactory@1edae2a8
Thread[Thread-1,5,main] Concurrency.DaemonFromFactory@104fa29e
Thread[Thread-0,5,main] Concurrency.DaemonFromFactory@144fe080
Thread[Thread-7,5,main] Concurrency.DaemonFromFactory@25144c3e
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
Thread[Thread-5,5,main] Concurrency.DaemonFromFactory@626007aa
Thread[Thread-9,5,main] Concurrency.DaemonFromFactory@1a7288d1
Thread[Thread-4,5,main] Concurrency.DaemonFromFactory@288523d
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
Thread[Thread-7,5,main] Concurrency.DaemonFromFactory@25144c3e
Thread[Thread-4,5,main] Concurrency.DaemonFromFactory@288523d
Thread[Thread-6,5,main] Concurrency.DaemonFromFactory@1edae2a8
Thread[Thread-1,5,main] Concurrency.DaemonFromFactory@104fa29e
Thread[Thread-0,5,main] Concurrency.DaemonFromFactory@144fe080
Thread[Thread-9,5,main] Concurrency.DaemonFromFactory@1a7288d1
Thread[Thread-5,5,main] Concurrency.DaemonFromFactory@626007aa
Thread[Thread-3,5,main] Concurrency.DaemonFromFactory@56214c1
Thread[Thread-2,5,main] Concurrency.DaemonFromFactory@5724147d
Thread[Thread-8,5,main] Concurrency.DaemonFromFactory@5b069a7f
4.首先应该意识到如果在用户线程突然退出的时候,那么后台线程在不执行finally子句的情况下就会终止其run方法。
/*当你调用这个程序的时候,你将看到finally子句不会执行,但是如果你注释掉对setDaemon()的调用,你将看到 * finally 子句将会执行. * 这种行为是正确的。即便你基于前面对finally给出的承诺,并不希望出现这种行为。但是情况就是这样 * 当最后一个非后台的线程终止的时候,后台线程就会突然的停止。因为一旦main()退出后,JVM就会立即关闭所有后台的 * 线程。因为你不能以优雅的方式来关闭后台线程,所以它们几乎不是一种好的思想。非后台的Executor通常是一种 * 更好的方式,因为Executor控制的所有的任务可以同时被关闭。*/ class ADaemon implements Runnable{ @Override public void run() { System.out.println("Starting ADaemon"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("Exiting via InterruptedException"); }finally { System.out.println("This should always run?"); } } } public class DaemonsDontRunFinally { public static void main(String[] args) { Thread t = new Thread(new ADaemon()); t.setDaemon(true); t.start(); } }最后的输出的结果如下:
Starting ADaemon
但是如果情况变为如下的情况,输出的结果又会不一样了:
class ADaemon implements Runnable{ @Override public void run() { System.out.println("Starting ADaemon"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("Exiting via InterruptedException"); }finally { System.out.println("This should always run?"); } } } public class DaemonsDontRunFinally { public static void main(String[] args) { Thread t = new Thread(new ADaemon()); t.setDaemon(true); t.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }由于主线程不是突然退出的,主线程在休眠期间,后台线程得到了执行的时间,所以最后的打印的结果为:
Starting ADaemon
This should always run?
This should always run?
更多推荐
已为社区贡献1条内容
所有评论(0)