Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護(hù)線程)
“專業(yè)、務(wù)實(shí)、高效、創(chuàng)新、把客戶的事當(dāng)成自己的事”是我們每一個(gè)人一直以來堅(jiān)持追求的企業(yè)文化。 創(chuàng)新互聯(lián)建站是您可以信賴的網(wǎng)站建設(shè)服務(wù)商、專業(yè)的互聯(lián)網(wǎng)服務(wù)提供商! 專注于成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、軟件開發(fā)、設(shè)計(jì)服務(wù)業(yè)務(wù)。我們始終堅(jiān)持以客戶需求為導(dǎo)向,結(jié)合用戶體驗(yàn)與視覺傳達(dá),提供有針對(duì)性的項(xiàng)目解決方案,提供專業(yè)性的建議,創(chuàng)新互聯(lián)建站將不斷地超越自我,追逐市場,引領(lǐng)市場!
用戶線程即運(yùn)行在前臺(tái)的線程,而守護(hù)線程是運(yùn)行在后臺(tái)的線程。 守護(hù)線程作用是為其他前臺(tái)線程的運(yùn)行提供便利服務(wù),而且僅在普通、非守護(hù)線程仍然運(yùn)行時(shí)才需要,比如垃圾回收線程就是一個(gè)守護(hù)線程。當(dāng)VM檢測僅剩一個(gè)守護(hù)線程,而用戶線程都已經(jīng)退出運(yùn)行時(shí),VM就會(huì)退出,因?yàn)闆]有如果沒有了被守護(hù)這,也就沒有繼續(xù)運(yùn)行程序的必要了。如果有非守護(hù)線程仍然存活,VM就不會(huì)退出。
守護(hù)線程并非只有虛擬機(jī)內(nèi)部提供,用戶在編寫程序時(shí)也可以自己設(shè)置守護(hù)線程。用戶可以用Thread的setDaemon(true)方法設(shè)置當(dāng)前線程為守護(hù)線程。
雖然守護(hù)線程可能非常有用,但必須小心確保其他所有非守護(hù)線程消亡時(shí),不會(huì)由于它的終止而產(chǎn)生任何危害。因?yàn)槟悴豢赡苤涝谒械挠脩艟€程退出運(yùn)行前,守護(hù)線程是否已經(jīng)完成了預(yù)期的服務(wù)任務(wù)。一旦所有的用戶線程退出了,虛擬機(jī)也就退出運(yùn)行了。 因此,不要在守護(hù)線程中執(zhí)行業(yè)務(wù)邏輯操作(比如對(duì)數(shù)據(jù)的讀寫等)。、
另外有幾點(diǎn)需要注意:
Timer代碼示例:
package day003; import java.util.Date; import java.util.TimerTask; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:MyTask * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午3:05:28 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午3:05:28 * 修改備注: * @version * */ public class MyTask extends TimerTask{ /** * (non-Javadoc) * @see java.util.TimerTask#run() */ public void run() { System.out.println("任務(wù)執(zhí)行了,時(shí)間為:"+new Date()); } } ----------------------------------------------------------------------------------- package day003; import java.util.Calendar; import java.util.Date; import java.util.Timer; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:TimerTaskRun * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午3:08:01 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午3:08:01 * 修改備注: * @version * */ public class TimerTaskRun { public static void main(String[] args) { System.out.println("系統(tǒng)當(dāng)前時(shí)間:"+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); } }
運(yùn)行結(jié)果:
系統(tǒng)當(dāng)前時(shí)間:Mon Mar 19 15:11:47 CST 2018
任務(wù)執(zhí)行了,時(shí)間為:Mon Mar 19 15:11:57 CST 2018
任務(wù)雖然運(yùn)行完了,但進(jìn)程還未銷毀,呈紅色狀態(tài),為什么會(huì)出現(xiàn)這種情況呢?
可以看一下Timer的源碼
/** * Creates a new timer. The associated thread does not * {@linkplain Thread#setDaemon run as a daemon}. */ public Timer() { this("Timer-" + serialNumber()); } /** * Creates a new timer whose associated thread has the specified name. * The associated thread does not * {@linkplain Thread#setDaemon run as a daemon}. * * @param name the name of the associated thread * @throws NullPointerException if {@code name} is null * @since 1.5 */ public Timer(String name) { thread.setName(name); thread.start(); }
可以看出每創(chuàng)建一個(gè)Timer就是啟動(dòng)一個(gè)新的線程,那么啟動(dòng)的線程不是守護(hù)線程,所以一直運(yùn)行。那我們?cè)撊绾螌?新創(chuàng)建的的Timer改成守護(hù)線程呢?更改如上的代碼:
package day003; import java.util.Calendar; import java.util.Date; import java.util.Timer; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:TimerTaskRun * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午3:08:01 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午3:08:01 * 修改備注: * @version * */ public class TimerTaskRun { public static void main(String[] args) { System.out.println("系統(tǒng)當(dāng)前時(shí)間:"+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); } }
運(yùn)行結(jié)果如下:
系統(tǒng)當(dāng)前時(shí)間:Mon Mar 19 15:21:42 CST 2018
守護(hù)線程中產(chǎn)生的線程也是守護(hù)線程
如下示例:
package day003; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:Daemon * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午3:30:53 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午3:30:53 * 修改備注: * @version * */ public class Daemon implements Runnable { private Thread[] t = new Thread[10]; /** * (non-Javadoc) * @see java.lang.Runnable#run() */ 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(); } } } ----------------------------------------------------------------------------------- package day003; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:DaemonSpawn * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午3:32:06 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午3:32:06 * 修改備注: * @version * */ public class DaemonSpawn implements Runnable { /** * (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { while (true) { Thread.yield(); } } } ----------------------------------------------------------------------------------- package day003; import java.util.concurrent.TimeUnit; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:DaemonRun * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午3:36:34 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午3:36:34 * 修改備注: * @version * */ public class DaemonRun { public static void main(String[] args) throws InterruptedException { Thread d = new Thread(new Daemon()); d.setDaemon(true);//必須在啟動(dòng)線程前調(diào)用 d.start(); System.out.println("d.isDaemon() = " + d.isDaemon() + "."); TimeUnit.SECONDS.sleep(1); } }
運(yùn)行結(jié)果如圖:
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.
如果將mian函數(shù)中的TimeUnit.SECONDS.sleep(1);注釋掉,看一下TimeUnit.SECONDS.sleep()的源碼:
/** * Performs a {@link Thread#sleep(long, int) Thread.sleep} using * this time unit. * This is a convenience method that converts time arguments into the * form required by the {@code Thread.sleep} method. * * @param timeout the minimum time to sleep. If less than * or equal to zero, do not sleep at all. * @throws InterruptedException if interrupted while sleeping */ public void sleep(long timeout) throws InterruptedException { if (timeout > 0) { long ms = toMillis(timeout); int ns = excessNanos(timeout, ms); Thread.sleep(ms, ns); } }
其實(shí)就是對(duì)Thread.sleep()的封裝,提供了可讀性更好的線程暫停操作
注釋后代碼運(yùn)行如下:
d.isDaemon() = true.
以上結(jié)果也說明了如果用戶線程全部退出了,只剩下守護(hù)線程存在了,虛擬機(jī)也就退出了。
典型的守護(hù)線程是(GC)垃圾回收線程。
package day003; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:MyThread * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午3:50:12 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午3:50:12 * 修改備注: * @version * */ public class MyThread extends Thread{ private int i = 0; /** * (non-Javadoc) * @see java.lang.Thread#run() */ public void run() { super.run(); try { while (true) { i++; System.out.println("i="+i); Thread.sleep(1000); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { MyThread daemonThread = new MyThread(); daemonThread.setDaemon(true); daemonThread.start(); Thread.sleep(5000); System.out.println("當(dāng)main線程執(zhí)行完畢,守護(hù)線程也停止了。"); } }
運(yùn)行結(jié)果:
i=1
i=2
i=3
i=4
i=5
當(dāng)main線程執(zhí)行完畢,守護(hù)線程也停止了。
除 JVM 內(nèi)部的守護(hù)線程外,用戶可以通過以下方法設(shè)置守護(hù)線程:
public final void setDaemon(boolean on)
可以通過以下方法查詢線程是否為守護(hù)線程:
public final boolean isDaemon()
關(guān)于守護(hù)線程的幾個(gè)要點(diǎn):
1、setDaemon 方法必須在 thread.start() 之前設(shè)置,否則會(huì)拋出 java.lang.IllegalThreadStateException 異常,不能將正在運(yùn)行的常規(guī)線程設(shè)置為守護(hù)線程
package day003; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:TestDaemon * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午4:01:32 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午4:01:32 * 修改備注: * @version * */ public class TestDaemon { public static void main(String[] args) { Thread thread = new Thread(); thread.start(); thread.setDaemon(true); } }
運(yùn)行結(jié)果:
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.setDaemon(Thread.java:1359)
at day003.TestDaemon.main(TestDaemon.java:32)
2、不是所有的應(yīng)用都可以分配給 Daemon 線程來進(jìn)行服務(wù),比如讀寫操作或者計(jì)算邏輯,因?yàn)樵?Daemon 線程還沒來的及進(jìn)行操作時(shí)虛擬機(jī)可能已經(jīng)退出了
package day003; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:TestDaemon2 * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午4:03:22 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午4:03:22 * 修改備注: * @version * */ public class TestDaemon2 extends Thread{ /** * (non-Javadoc) * @see java.lang.Thread#run() */ public void run() { super.run(); FileOutputStream outputStream = null; try { Thread.sleep(3000); File file = new File("daemon.txt"); outputStream = new FileOutputStream(file); outputStream.write("daemon".getBytes()); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Thread thread = new TestDaemon2(); thread.setDaemon(true); thread.start(); }
以上代碼中線程功能是向工程根目錄的“daemon.txt”文件寫入字符串“daemon”,實(shí)際運(yùn)行結(jié)果發(fā)現(xiàn)并未成功寫入,且未報(bào)任何錯(cuò)誤,原因是寫入文件的線程被設(shè)置為守護(hù)線程,該線程還在 sleep 過程中時(shí)所有用戶線程就全部結(jié)束了,守護(hù)線程也會(huì)隨著 JVM 一起退出。
如果將上面代碼中的thread.setDaemon(true);注釋掉,
public static void main(String[] args) { Thread thread = new TestDaemon2(); //thread.setDaemon(true); thread.start(); }
不將線程設(shè)置為守護(hù)線程可以在工程根目錄的“daemon.txt”文件中看到字符串“daemon”
示例2:
package day003; /** * * 項(xiàng)目名稱:JavaThread * 類名稱:CustomThread * 類描述: * 創(chuàng)建人:liuc * 創(chuàng)建時(shí)間:2018年3月19日 下午4:16:42 * 修改人:liuc * 修改時(shí)間:2018年3月19日 下午4:16:42 * 修改備注: * @version * */ public class CustomThread extends Thread { /** * (non-Javadoc) * @see java.lang.Thread#run() */ public void run() { super.run(); for (int i = 0; i < 100; i++) { System.out.println("Daemon Thread : " + i); } } public static void main(String[] args) { Thread daemonThread = new CustomThread(); daemonThread.setDaemon(true); Thread userThread = new Thread(); daemonThread.start(); userThread.start(); } }
多次執(zhí)行示例2代碼,控制臺(tái)要么不打印任何信息,要么打印一部分循環(huán)的輸出信息就結(jié)束了,從運(yùn)行結(jié)果可以看出,守護(hù)線程并未執(zhí)行完成所有循環(huán)就結(jié)束了,因?yàn)橛脩艟€程在守護(hù)線程執(zhí)行循環(huán)的過程中就已全部結(jié)束,守護(hù)線程也隨著 JVM 一起結(jié)束。
以上所述是小編給大家介紹的java守護(hù)線程與非守護(hù)線程詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)創(chuàng)新互聯(lián)網(wǎng)站的支持!