Java中后臺(tái)線程實(shí)例解析
本文研究的主要是Java中后臺(tái)線程的相關(guān)問題,具體介紹如下。
以前從來沒有聽說過,java中有后臺(tái)線程這種東西。一般來說,JVM(JAVA虛擬機(jī))中一般會(huì)包括倆種線程,分別是用戶線程和后臺(tái)線程。所謂后臺(tái)線程(daemon)線程指的是:在程序運(yùn)行的時(shí)候在后臺(tái)提供的一種通用的服務(wù)的線程,并且這種線程并不屬于程序中不可或缺的部分。因此,當(dāng)所有的非后臺(tái)線程結(jié)束的時(shí)候,也就是用戶線程都結(jié)束的時(shí)候,程序也就終止了。同時(shí),會(huì)殺死進(jìn)程中的所有的后臺(tái)線程。反過來說,只要有任何非后臺(tái)線程還在運(yùn)行,程序就不會(huì)結(jié)束。不如執(zhí)行main()的就是一個(gè)非后臺(tái)線程。
基于這個(gè)特點(diǎn),當(dāng)虛擬機(jī)中的用戶線程全部退出運(yùn)行時(shí),守護(hù)線程沒有服務(wù)的對(duì)象后,JVM也就退出了。
這點(diǎn)JDK源碼中的介紹已經(jīng)說明這一點(diǎn)了。
* 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.后臺(tái)線程的啟動(dòng)的條件:
/*必須在啟動(dòng)線程之前調(diào)用SetDaemon()方法,才能把這個(gè)線程設(shè)置為后臺(tái)線程。 * 在這個(gè)程序里面,當(dāng)我們輸入了字符串以后,那么Main線程就會(huì)停止運(yùn)行了 * 那么程序中已經(jīng)沒有可以運(yùn)行的用戶的線程了。所以后臺(tái)線程就會(huì)被停止了 * JVM也就會(huì)被停停止了,感興趣的讀者可以自己嘗試一下*/ public class DaemonRunner implements Runnable { @Override public void run() { while (true) { for (int i = 0; i < 3; i++) { System.out.println("守護(hù)線程" + 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.在后臺(tái)線程中啟動(dòng)的線程都屬于后臺(tái)線程。盡管你沒有明確指明它們是后臺(tái)線程,但是它們的確是后臺(tái)線程。
/*可以通過調(diào)用isDaemon()方法來確定線程是否是一個(gè)后臺(tái)線程。如果是一個(gè)后臺(tái)線程, * 那么它創(chuàng)建的任何線程都被自動(dòng)設(shè)置成后臺(tái)的線程 * 在這個(gè)實(shí)例中,Daemon線程被設(shè)置成了后臺(tái)模式,然后派生出許多子線程,這些線程并沒有被設(shè)置成 * 后臺(tái)模式,不過它們的確是后臺(tái)線程。接著,Daemon線程進(jìn)入了無限循環(huán),并且在循環(huán)里調(diào)用了yield方法 * 把控制權(quán)交給其它的線程或者進(jìn)程*/ 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); //讓啟動(dòng)的后臺(tái)的線程可以獲得一定的執(zhí)行的時(shí)間。 } catch (InterruptedException e) { e.printStackTrace(); } } }
最后的執(zhí)行的結(jié)果如下:
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
3.通過為Executors.newCachedThreadPool()
方法指定一個(gè)ThreadFactory的對(duì)象。通過這種方法,我們也可以將
我們想要啟動(dòng)的線程設(shè)置為后臺(tái)線程。
/*在這個(gè)例子中,對(duì)于這個(gè)靜態(tài)的構(gòu)造方法:Executors.newCachedThreadPool(new DaemonThreadFactory() * 我們可以為傳入一個(gè)ThreadFactory的對(duì)象,那么我們就可以通過這種方法,將我們想要啟動(dòng)的線程設(shè)置為后臺(tái)線程 * 這是要注意的。*/ class DaemonThreadFactory implements ThreadFactory{ @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t; } } /*在這個(gè)例子中,在Main方法中,會(huì)首先調(diào)用Main方法中的普通的方法,“ System.out.println("All dameons started");” * 所以會(huì)首先打印這一條語句。然后在主線程休眠期間,相應(yīng)的后臺(tái)線程,就會(huì)獲得執(zhí)行的時(shí)間,最后在Main線程 * 結(jié)束了運(yùn)行的時(shí)候,也就是當(dāng)Main線程從休眠中恢復(fù)過來的時(shí)候,那么Main線性就會(huì)結(jié)束運(yùn)行。接著, * 那么所有的后臺(tái)的線程都會(huì)停止。JVM也會(huì)停止執(zhí)行。*/ 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(); } } }
最后的輸出的結(jié)果為:
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
4.首先應(yīng)該意識(shí)到如果在用戶線程突然退出的時(shí)候,那么后臺(tái)線程在不執(zhí)行finally子句的情況下就會(huì)終止其run方法。
/*當(dāng)你調(diào)用這個(gè)程序的時(shí)候,你將看到finally子句不會(huì)執(zhí)行,但是如果你注釋掉對(duì)setDaemon()的調(diào)用,你將看到 * finally 子句將會(huì)執(zhí)行. * 這種行為是正確的。即便你基于前面對(duì)finally給出的承諾,并不希望出現(xiàn)這種行為。但是情況就是這樣 * 當(dāng)最后一個(gè)非后臺(tái)的線程終止的時(shí)候,后臺(tái)線程就會(huì)突然的停止。因?yàn)橐坏﹎ain()退出后,JVM就會(huì)立即關(guān)閉所有后臺(tái)的 * 線程。因?yàn)槟悴荒芤詢?yōu)雅的方式來關(guān)閉后臺(tái)線程,所以它們幾乎不是一種好的思想。非后臺(tái)的Executor通常是一種 * 更好的方式,因?yàn)镋xecutor控制的所有的任務(wù)可以同時(shí)被關(guān)閉。*/ 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(); } }
最后的輸出的結(jié)果如下:
Starting ADaemon
但是如果情況變?yōu)槿缦碌那闆r,輸出的結(jié)果又會(huì)不一樣了:
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(); } } }
由于主線程不是突然退出的,主線程在休眠期間,后臺(tái)線程得到了執(zhí)行的時(shí)間,所以最后的打印的結(jié)果為:
Starting ADaemon
This should always run?
總結(jié)
以上就是本文關(guān)于Java中后臺(tái)線程實(shí)例解析的全部內(nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
- Java并發(fā)編程示例(七):守護(hù)線程的創(chuàng)建和運(yùn)行
- Java線程之守護(hù)線程(Daemon)用法實(shí)例
- 淺談java的守護(hù)線程與非守護(hù)線程
- 淺談java中守護(hù)線程與用戶線程
- 簡單了解Java編程中線程的創(chuàng)建與守護(hù)線程
- Java守護(hù)線程實(shí)例詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- Java語言多線程終止中的守護(hù)線程實(shí)例
- Java 守護(hù)線程_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- 詳解Java線程-守護(hù)線程與用戶線程
- 【java 多線程】守護(hù)線程與非守護(hù)線程的詳解
- Java后臺(tái)線程操作示例【守護(hù)線程】
相關(guān)文章
feign GET請(qǐng)求不支持對(duì)象傳參的坑及解決
這篇文章主要介紹了feign GET請(qǐng)求不支持對(duì)象傳參的坑及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03springboot webflux 過濾器(使用RouterFunction實(shí)現(xiàn))
這篇文章主要介紹了springboot webflux 過濾器(使用RouterFunction實(shí)現(xiàn)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03SpringBoot+Ant Design Vue實(shí)現(xiàn)數(shù)據(jù)導(dǎo)出功能方式
這篇文章主要介紹了SpringBoot+Ant Design Vue實(shí)現(xiàn)數(shù)據(jù)導(dǎo)出功能方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java實(shí)現(xiàn)貪吃蛇大作戰(zhàn)小游戲(附源碼)
今天給大家?guī)淼氖切№?xiàng)目是 基于Java+Swing+IO流實(shí)現(xiàn) 的貪吃蛇大作戰(zhàn)小游戲。實(shí)現(xiàn)了界面可視化、基本的吃食物功能、死亡功能、移動(dòng)功能、積分功能,并額外實(shí)現(xiàn)了主動(dòng)加速和鼓勵(lì)機(jī)制,需要的可以參考一下2022-07-07關(guān)于Mybatis中SQL節(jié)點(diǎn)的深入解析
這篇文章主要給大家介紹了關(guān)于Mybatis中SQL節(jié)點(diǎn)的深入解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03從Myeclipse 導(dǎo)入到eclipse中無法識(shí)別為 web項(xiàng)目 問題的解決步驟
這篇文章主要介紹了從Myeclipse 導(dǎo)入到eclipse中無法識(shí)別為 web項(xiàng)目 問題的解決步驟,需要的朋友可以參考下2018-05-05