本文研究的主要是Java中後台線程的相關問題,具體介紹如下。
以前從來沒有聽說過,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
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
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?
以上就是本文關於Java中後台線程實例解析的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!