您好,登錄后才能下訂單哦!
本文研究的主要是Java中后臺線程的相關(guān)問題,具體介紹如下。
以前從來沒有聽說過,java中有后臺線程這種東西。一般來說,JVM(JAVA虛擬機)中一般會包括倆種線程,分別是用戶線程和后臺線程。所謂后臺線程(daemon)線程指的是:在程序運行的時候在后臺提供的一種通用的服務(wù)的線程,并且這種線程并不屬于程序中不可或缺的部分。因此,當(dāng)所有的非后臺線程結(jié)束的時候,也就是用戶線程都結(jié)束的時候,程序也就終止了。同時,會殺死進(jìn)程中的所有的后臺線程。反過來說,只要有任何非后臺線程還在運行,程序就不會結(jié)束。不如執(zhí)行main()的就是一個非后臺線程。
基于這個特點,當(dāng)虛擬機中的用戶線程全部退出運行時,守護(hù)線程沒有服務(wù)的對象后,JVM也就退出了。
這點JDK源碼中的介紹已經(jīng)說明這一點了。
* 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.后臺線程的啟動的條件:
/*必須在啟動線程之前調(diào)用SetDaemon()方法,才能把這個線程設(shè)置為后臺線程。 * 在這個程序里面,當(dāng)我們輸入了字符串以后,那么Main線程就會停止運行了 * 那么程序中已經(jīng)沒有可以運行的用戶的線程了。所以后臺線程就會被停止了 * JVM也就會被停停止了,感興趣的讀者可以自己嘗試一下*/ 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.在后臺線程中啟動的線程都屬于后臺線程。盡管你沒有明確指明它們是后臺線程,但是它們的確是后臺線程。
/*可以通過調(diào)用isDaemon()方法來確定線程是否是一個后臺線程。如果是一個后臺線程, * 那么它創(chuàng)建的任何線程都被自動設(shè)置成后臺的線程 * 在這個實例中,Daemon線程被設(shè)置成了后臺模式,然后派生出許多子線程,這些線程并沒有被設(shè)置成 * 后臺模式,不過它們的確是后臺線程。接著,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); //讓啟動的后臺的線程可以獲得一定的執(zhí)行的時間。 } 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()
方法指定一個ThreadFactory的對象。通過這種方法,我們也可以將
我們想要啟動的線程設(shè)置為后臺線程。
/*在這個例子中,對于這個靜態(tài)的構(gòu)造方法:Executors.newCachedThreadPool(new DaemonThreadFactory() * 我們可以為傳入一個ThreadFactory的對象,那么我們就可以通過這種方法,將我們想要啟動的線程設(shè)置為后臺線程 * 這是要注意的。*/ class DaemonThreadFactory implements ThreadFactory{ @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t; } } /*在這個例子中,在Main方法中,會首先調(diào)用Main方法中的普通的方法,“ System.out.println("All dameons started");” * 所以會首先打印這一條語句。然后在主線程休眠期間,相應(yīng)的后臺線程,就會獲得執(zhí)行的時間,最后在Main線程 * 結(jié)束了運行的時候,也就是當(dāng)Main線程從休眠中恢復(fù)過來的時候,那么Main線性就會結(jié)束運行。接著, * 那么所有的后臺的線程都會停止。JVM也會停止執(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)該意識到如果在用戶線程突然退出的時候,那么后臺線程在不執(zhí)行finally子句的情況下就會終止其run方法。
/*當(dāng)你調(diào)用這個程序的時候,你將看到finally子句不會執(zhí)行,但是如果你注釋掉對setDaemon()的調(diào)用,你將看到 * finally 子句將會執(zhí)行. * 這種行為是正確的。即便你基于前面對finally給出的承諾,并不希望出現(xiàn)這種行為。但是情況就是這樣 * 當(dāng)最后一個非后臺的線程終止的時候,后臺線程就會突然的停止。因為一旦main()退出后,JVM就會立即關(guān)閉所有后臺的 * 線程。因為你不能以優(yōu)雅的方式來關(guān)閉后臺線程,所以它們幾乎不是一種好的思想。非后臺的Executor通常是一種 * 更好的方式,因為Executor控制的所有的任務(wù)可以同時被關(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é)果又會不一樣了:
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(); } } }
由于主線程不是突然退出的,主線程在休眠期間,后臺線程得到了執(zhí)行的時間,所以最后的打印的結(jié)果為:
Starting ADaemon
This should always run?
以上就是本文關(guān)于Java中后臺線程實例解析的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。