最近在看多線程的Timer章節,發現運用到了守護線程,感受Java的基礎知識仍是須要補充。java
Java分爲兩種線程:用戶線程和守護線程數據庫
所謂守護線程是指在程序運行的時候在後臺提供一種通用服務的線程,好比垃圾回收線程就是一個很稱職的守護者,而且這種線程並不屬於程序中不可或缺的部分。因 此,當全部的非守護線程結束時,程序也就終止了,同時會殺死進程中的全部守護線程。反過來講,只要任何非守護線程還在運行,程序就不會終止。多線程
守護線程和用戶線程的沒啥本質的區別:惟一的不一樣之處就在於虛擬機的離開:若是用戶線程已經所有退出運行了,只剩下守護線程存在了,虛擬機也就退出了。 由於沒有了被守護者,守護線程也就沒有工做可作了,也就沒有繼續運行程序的必要了。ide
將線程轉換爲守護線程能夠經過調用Thread對象的setDaemon(true)方法來實現。在使用守護線程時須要注意一下幾點:函數
(1) thread.setDaemon(true)必須在thread.start()以前設置,不然會跑出一個IllegalThreadStateException異常。你不能把正在運行的常規線程設置爲守護線程。this
(2) 在Daemon線程中產生的新線程也是Daemon的。線程
(3) 守護線程應該永遠不去訪問固有資源,如文件、數據庫,由於它會在任什麼時候候甚至在一個操做的中間發生中斷。code
Timer代碼示例:對象
import java.util.Date; import java.util.TimerTask; public class MyTask extends TimerTask { @Override public void run() { System.out.println("任務執行了,時間爲:"+new Date()); }
主函數進程
import java.util.Calendar; import java.util.Date; import java.util.Timer; public class Test1 { public static void main(String[] args){ System.out.println("當前時間:"+new Date()); Calendar calendar=Calendar.getInstance(); calendar.add(Calendar.SECOND,10); Date date=calendar.getTime(); MyTask task=new MyTask(); Timer timer=new Timer(); timer.schedule(task,date); } }
運行結果:
當前時間:Sat Jun 03 11:47:40 CST 2017 任務執行了,時間爲:Sat Jun 03 11:47:50 CST 2017
任務雖然運行完了,但進程還未銷燬,呈紅色狀態,爲何會出現這種狀況呢?
能夠看一下Timer的源碼
public Timer() { this("Timer-" + serialNumber()); } public Timer(String name) { thread.setName(name); thread.start(); }
能夠看出每建立一個Timer就是啓動一個新的線程,那麼啓動的線程不是守護線程,因此一直運行。將新建立的的Timer改爲守護線程,更改如上的代碼:
import java.util.Calendar; import java.util.Date; import java.util.Timer; public class Test1 { public static void main(String[] args){ System.out.println("當前時間:"+new Date()); Calendar calendar=Calendar.getInstance(); calendar.add(Calendar.SECOND,10); Date date=calendar.getTime(); MyTask task=new MyTask(); Timer timer=new Timer(true); timer.schedule(task,date); } }
運行結果以下:
當前時間:Sat Jun 03 11:47:40 CST 2017
守護線程中產生的線程也是守護線程
以下示例:
public 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(); } } }
類DaemonSpawn:
public class DaemonSpawn implements Runnable { @Override public void run() { while (true) { Thread.yield(); } } }
主函數:
import java.util.concurrent.TimeUnit; public class Test1 { public static void main(String[] args) throws InterruptedException { Thread d = new Thread(new Daemon()); d.setDaemon(true); //必須在啓動線程前調用 d.start(); System.out.println("d.isDaemon() = " + d.isDaemon() + "."); TimeUnit.SECONDS.sleep(1); } }
運行結果如圖:
d.isDaemon() = true. DaemonSpawn 0 started. DaemonSpawn 1 started. DaemonSpawn 2 started. DaemonSpawn 3 started. DaemonSpawn 4 started. DaemonSpawn 5 started. DaemonSpawn 6 started. DaemonSpawn 7 started. DaemonSpawn 8 started. DaemonSpawn 9 started. t[0].isDaemon() = true. t[1].isDaemon() = true. t[2].isDaemon() = true. t[3].isDaemon() = true. t[4].isDaemon() = true. t[5].isDaemon() = true. t[6].isDaemon() = true. t[7].isDaemon() = true. t[8].isDaemon() = true. t[9].isDaemon() = true. Process finished with exit code 0
若是將mian函數中的TimeUnit.SECONDS.sleep(1);註釋掉,看一下TimeUnit.SECONDS.sleep()的源碼:
public void sleep(long timeout) throws InterruptedException { if (timeout > 0) { long ms = toMillis(timeout); int ns = excessNanos(timeout, ms); Thread.sleep(ms, ns); } }
其實就是對Thread.sleep()的封裝,提供了可讀性更好的線程暫停操做
註釋後代碼運行以下:
d.isDaemon() = true. DaemonSpawn 0 started. DaemonSpawn 1 started. DaemonSpawn 2 started. DaemonSpawn 3 started. DaemonSpawn 4 started. DaemonSpawn 5 started. DaemonSpawn 6 started. DaemonSpawn 7 started. DaemonSpawn 8 started. DaemonSpawn 9 started.
以上結果也說明了若是用戶線程所有退出了,只剩下守護線程存在了,虛擬機也就退出了。