Java中守護(hù)線程的總結(jié)

前言

在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護(hù)線程)

用個(gè)比較通俗的比如,任何一個(gè)守護(hù)線程都是整個(gè)JVM中所有非守護(hù)線程的保姆:

只要當(dāng)前JVM實(shí)例中尚存在任何一個(gè)非守護(hù)線程沒有結(jié)束,守護(hù)線程就全部工作;只有當(dāng)最后一個(gè)非守護(hù)線程結(jié)束時(shí),守護(hù)線程隨著JVM一同結(jié)束工作。
Daemon的作用是為其他線程的運(yùn)行提供便利服務(wù),守護(hù)線程最典型的應(yīng)用就是 GC (垃圾回收器),它就是一個(gè)很稱職的守護(hù)者。

User和Daemon兩者幾乎沒有區(qū)別,唯一的不同之處就在于虛擬機(jī)的離開:如果 User Thread已經(jīng)全部退出運(yùn)行了,只剩下Daemon Thread存在了,虛擬機(jī)也就退出了。 因?yàn)闆]有了被守護(hù)者,Daemon也就沒有工作可做了,也就沒有繼續(xù)運(yùn)行程序的必要了。


用法

值得一提的是,守護(hù)線程并非只有虛擬機(jī)內(nèi)部提供,用戶在編寫程序時(shí)也可以自己設(shè)置守護(hù)線程。下面的方法就是用來(lái)設(shè)置守護(hù)線程的。

Thread daemonTread = new Thread();
 
  // 設(shè)定 daemonThread 為 守護(hù)線程,default false(非守護(hù)線程)
 daemonThread.setDaemon(true);
 
 // 驗(yàn)證當(dāng)前線程是否為守護(hù)線程,返回 true 則為守護(hù)線程
 daemonThread.isDaemon();

這里有幾點(diǎn)需要注意:

  • thread.setDaemon(true)必須在thread.start()之前設(shè)置,否則會(huì)跑出一個(gè)IllegalThreadStateException異常。你不能把正在運(yùn)行的常規(guī)線程設(shè)置為守護(hù)線程。
  • 在Daemon線程中產(chǎn)生的新線程也是Daemon的。
  • 不要認(rèn)為所有的應(yīng)用都可以分配給Daemon來(lái)進(jìn)行服務(wù),比如讀寫操作或者計(jì)算邏輯。

因?yàn)槟悴豢赡苤涝谒械腢ser完成之前,Daemon是否已經(jīng)完成了預(yù)期的服務(wù)任務(wù)。一旦User退出了,可能大量數(shù)據(jù)還沒有來(lái)得及讀入或?qū)懗?,?jì)算任務(wù)也可能多次運(yùn)行結(jié)果不一樣。這對(duì)程序是毀滅性的。造成這個(gè)結(jié)果理由已經(jīng)說(shuō)過(guò)了:一旦所有User Thread離開了,虛擬機(jī)也就退出運(yùn)行了。


//完成文件輸出的守護(hù)線程任務(wù)
import java.io.*;   
  
class TestRunnable implements Runnable{   
    public void run(){   
               try{   
                  Thread.sleep(1000);//守護(hù)線程阻塞1秒后運(yùn)行   
                  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); //設(shè)置守護(hù)線程   
        thread.start(); //開始執(zhí)行分進(jìn)程   
    }   
}   
//運(yùn)行結(jié)果:文件daemon.txt中沒有"daemon"字符串。

看到了吧,把輸入輸出邏輯包裝進(jìn)守護(hù)線程多么的可怕,字符串并沒有寫入指定文件。原因也很簡(jiǎn)單,直到主線程完成,守護(hù)線程仍處于1秒的阻塞狀態(tài)。這個(gè)時(shí)候主線程很快就運(yùn)行完了,虛擬機(jī)退出,Daemon停止服務(wù),輸出操作自然失敗了。


public class Test {
  public static void main(String args) {
      Thread t1 = new MyCommon();
      Thread t2 = new Thread(new MyDaemon());
      t2.setDaemon(true); //設(shè)置為守護(hù)線程
      t2.start();
      t1.start();
      }
  }
  
  class MyCommon extends Thread {
      public void run() {
      for (int i = 0; i < 5; i++) {
      System.out.println("線程1第" + i + "次執(zhí)行!");
          try {
              Thread.sleep(7);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
      }
  }
}

class MyDaemon implements Runnable {
  public void run() {
      for (long i = 0; i < 9999999L; i++) {
      System.out.println("后臺(tái)線程第" + i + "次執(zhí)行!");
      try {
          Thread.sleep(7);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
  }
}
    后臺(tái)線程第0次執(zhí)行!
  線程1第0次執(zhí)行! 
  線程1第1次執(zhí)行! 
  后臺(tái)線程第1次執(zhí)行! 
  后臺(tái)線程第2次執(zhí)行! 
  線程1第2次執(zhí)行! 
  線程1第3次執(zhí)行! 
  后臺(tái)線程第3次執(zhí)行! 
  線程1第4次執(zhí)行! 
  后臺(tái)線程第4次執(zhí)行! 
  后臺(tái)線程第5次執(zhí)行! 
  后臺(tái)線程第6次執(zhí)行! 
  后臺(tái)線程第7次執(zhí)行! 
  Process finished with exit code 0 

從上面的執(zhí)行結(jié)果可以看出:
  前臺(tái)線程是保證執(zhí)行完畢的,后臺(tái)線程還沒有執(zhí)行完畢就退出了。
  實(shí)際上:JRE判斷程序是否執(zhí)行結(jié)束的標(biāo)準(zhǔn)是所有的前臺(tái)執(zhí)線程行完畢了,而不管后臺(tái)線程的狀態(tài),因此,在使用后臺(tái)縣城時(shí)候一定要注意這個(gè)問(wèn)題。


補(bǔ)充:

定義:守護(hù)線程--也稱“服務(wù)線程”,在沒有用戶線程可服務(wù)時(shí)會(huì)自動(dòng)離開。

優(yōu)先級(jí):守護(hù)線程的優(yōu)先級(jí)比較低,用于為系統(tǒng)中的其它對(duì)象和線程提供服務(wù)。

設(shè)置:通過(guò)setDaemon(true)來(lái)設(shè)置線程為“守護(hù)線程”;將一個(gè)用戶線程設(shè)置為
守護(hù)線程的方式是在 線程對(duì)象創(chuàng)建 之前 用線程對(duì)象的setDaemon方法。

example: 垃圾回收線程就是一個(gè)經(jīng)典的守護(hù)線程,當(dāng)我們的程序中不再有任何運(yùn)行的Thread,程序就不會(huì)再產(chǎn)生垃圾,垃圾回收器也就無(wú)事可做,所以當(dāng)垃圾回收線程是JVM上僅剩的線程時(shí),垃圾回收線程會(huì)自動(dòng)離開。它始終在低級(jí)別的狀態(tài)中運(yùn)行,用于實(shí)時(shí)監(jiān)控和管理系統(tǒng)中的可回收資源。

生命周期:守護(hù)進(jìn)程(Daemon)是運(yùn)行在后臺(tái)的一種特殊進(jìn)程。它獨(dú)立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。也就是說(shuō)守護(hù)線程不依賴于終端,但是依賴于系統(tǒng),與系統(tǒng)“同生共死”。那Java的守護(hù)線程是什么樣子的呢。當(dāng)JVM中所有的線程都是守護(hù)線程的時(shí)候,JVM就可以退出了;如果還有一個(gè)或以上的非守護(hù)線程則JVM不會(huì)退出。

這里寫圖片描述
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容