守護線程與非守護線程
最近在看多線程的Timer章節,發現運用到了守護線程,感覺Java的基礎知識還是需要補充。
Java分為兩種線程:用戶線程和守護線程
所謂守護線程是指在程序運行的時候在后台提供一種通用服務的線程,比如垃圾回收線程就是一個很稱職的守護者,並且這種線程並不屬於程序中不可或缺的部分。因 此,當所有的非守護線程結束時,程序也就終止了,同時會殺死進程中的所有守護線程。反過來說,只要任何非守護線程還在運行,程序就不會終止。
守護線程和用戶線程的沒啥本質的區別:唯一的不同之處就在於虛擬機的離開:如果用戶線程已經全部退出運行了,只剩下守護線程存在了,虛擬機也就退出了。 因為沒有了被守護者,守護線程也就沒有工作可做了,也就沒有繼續運行程序的必要了。
將線程轉換為守護線程可以通過調用Thread對象的setDaemon(true)方法來實現。在使用守護線程時需要注意一下幾點:
(1) thread.setDaemon(true)必須在thread.start()之前設置,否則會跑出一個IllegalThreadStateException異常。你不能把正在運行的常規線程設置為守護線程。
(2) 在Daemon線程中產生的新線程也是Daemon的。
(3) 守護線程應該永遠不去訪問固有資源,如文件、數據庫,因為它會在任何時候甚至在一個操作的中間發生中斷。
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.
以上結果也說明了如果用戶線程全部退出了,只剩下守護線程存在了,虛擬機也就退出了。