java中的守護線程


在Java中有兩類線程,分別是User Thread(用戶線程)和Daemon Thread(守護線程) 。

用戶線程很好理解,我們日常開發中編寫的業務邏輯代碼,運行起來都是一個個用戶線程。而守護線程相對來說則要特別理解一下。

什么是守護線程

在操作系統里面是沒有所謂的守護線程的概念的,只有守護進程一說。但是Java語言機制是構建在JVM的基礎之上的,這一機制意味着Java平台是把操作系統的底層給屏蔽了起來,所以它可以在它自己的虛擬的平台里面構造出對自己有利的機制。而Java語言或者說平台的設計者多多少少是收到Unix操作系統思想的影響,而守護線程機制又是對JVM這樣的平台湊合,於是守護線程應運而生。

所謂的守護線程,指的是程序運行時在后台提供的一種通用服務的線程。比如垃圾回收線程就是一個很稱職的守護者,並且這種線程並不屬於程序中不可或缺的部分。因此,當所有的非守護線程結束時,程序也就終止了,同時會殺死進程中的所有守護線程。反過來說,只要任何非守護線程還在運行,程序就不會終止。

事實上,User Thread(用戶線程)和Daemon Thread(守護線程)從本質上來說並沒有什么區別,唯一的不同之處就在於虛擬機的離開:如果用戶線程已經全部退出運行了,只剩下守護線程存在了,虛擬機也就退出了。 因為沒有了被守護者,守護線程也就沒有工作可做了,也就沒有繼續運行程序的必要了。

守護線程的使用與注意事項

守護線程並非只有虛擬機內部可以提供,用戶也可以手動將一個用戶線程設定/轉換為守護線程。

在Thread類中提供了一個setDaemon(true)方法來將一個普通的線程(用戶線程)設置為守護線程。

public final void setDaemon(boolean on);

在使用的過程中,有幾點需要注意:

1.thread.setDaemon(true)必須在thread.start()之前設置,否則會拋出一個IllegalThreadStateException異常。這也就意味着不能把正在運行的常規線程設置為守護線程。 這點與操作系統中的守護進程有着明顯的區別,守護進程是創建后,讓進程擺脫原會話的控制+讓進程擺脫原進程組的控制+讓進程擺脫原控制終端的控制;所以說寄托於虛擬機的語言機制跟系統級語言有着本質上面的區別。

2.在Daemon線程中產生的新線程也是Daemon的。關於這一點又是與操作系統中的守護進程有着本質的區別:守護進程fork()出來的子進程不再是守護進程,盡管它把父進程的進程相關信息復制過去了,但是子進程的進程的父進程不是init進程,所謂的守護進程本質上說就是,當父進程掛掉,init就會收養該進程,然后文件0、1和2都是/dev/null,當前目錄到/。

3.不是所有的應用都可以分配給Daemon線程來進行服務的,比如讀寫操作或者計算邏輯。因為這種應用可能在Daemon Thread還沒來得及進行操作時,虛擬機已經退出了。這也就意味着,守護線程應該永遠不去訪問固有資源,如文件、數據庫,因為它會在任何時候甚至在一個操作的中間發生中斷。

下面以一個完成文件輸出的守護線程任務作為例子:

import java.io.*;  

class TestRunnable implements Runnable {
    public void run(){
        try {
            Thread.sleep(1000); // 守護線程阻塞1秒后運行  
            File f = new File("daemon.txt");
            FileOutputStream os = new FileOutputStream(f,true);
            os.write("daemon".getBytes());
        } catch(IOException e1) {  
            e1.printStackTrace();  
        } catch(InterruptedException e2) {  
            e2.printStackTrace();  
        }  
    }  
}  

public class TestDemo2 {
    public static void main(String[] args) throws InterruptedException {
        Runnable tr = new TestRunnable();
        Thread thread = new Thread(tr);
        thread.setDaemon(true); // 設置守護線程(必須在thread.start()之前)
        thread.start(); // 開始執行分進程
    }
}

上面這段代碼的運行結果是文件daemon.txt中沒有daemon字符串。

但是如果把thread.setDaemon(true);這行代碼注釋掉,文件daemon.txt是可以被寫入daemon字符串的,因為這個時候這個線程就是普通的用戶線程了。

簡單理解就是,JRE判斷程序是否執行結束的標准是所有的前台線程(用戶線程)執行完畢了,而不管后台線程(守護線程)的狀態。

守護線程的應用場景

前面說了那么多,那么Daemon Thread的實際應用在那里呢?舉個例子,Web服務器中的Servlet,在容器啟動時,后台都會初始化一個服務線程,即調度線程,負責處理http請求,然后每個請求過來,調度線程就會從線程池中取出一個工作者線程來處理該請求,從而實現並發控制的目的。也就是說,一個實際應用在Java的線程池中的調度線程。

總結

從我的理解,守護線程就是用來告訴JVM,我的這個線程是一個低級別的線程,不需要等待它運行完才退出,讓JVM喜歡什么時候退出就退出,不用管這個線程。

在日常的業務相關的CRUD開發中,其實並不會關注到守護線程這個概念,也幾乎不會用上。

但是如果要往更高的地方走的話,這些深層次的概念還是要了解一下的,比如一些框架的底層實現。

 

"我不知道我有多喜歡你,但如果是去見你,我一定用跑的。"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM