1. 程式人生 > >守護執行緒、非守護執行緒簡介和addShutdownHook方法使用

守護執行緒、非守護執行緒簡介和addShutdownHook方法使用

 

1,首先什麼是守護執行緒,什麼是非守護執行緒呢

下面是網上資料總結如下:

Java有兩種Thread:“守護執行緒Daemon”(守護執行緒)與“使用者執行緒User”(非守護執行緒)。

從字面上我們很容易將守護執行緒理解成是由虛擬機器(virtual machine)在內部建立的,而使用者執行緒則是自己所建立的。事實並不是這樣,任何執行緒都可以是“守護執行緒Daemon”或“使用者執行緒User”。他們在幾乎每個方面都是相同的,唯一的區別是判斷虛擬機器何時離開:

使用者執行緒:Java虛擬機器在它所有非守護執行緒已經離開後自動離開。

守護執行緒:守護執行緒則是用來服務使用者執行緒的,如果沒有其他使用者執行緒在執行,那麼就沒有可服務物件,也就沒有理由繼續下去。

守護執行緒與普通執行緒的唯一區別是:當JVM中所有的執行緒都是守護執行緒的時候,JVM就可以退出了;如果還有一個或以上的非守護執行緒則不會退出。(以上是針對正常退出,呼叫System.exit則必定會退出)  

當然我們也可以把一個執行緒設定成一個守護執行緒 方法就是 物件執行緒.setDaemon(true);

舉個例子:就像   天上人間的保安 (守護執行緒),裡面有牌位姑娘(非守護執行緒),他們是可以同時幹著各自的活兒,但是 姑娘們要是都被JC帶走了,那麼門口的保安也就沒有存在的意義了。

 2,下面介紹addShutdownHook方法註冊新的虛擬機器來關閉鉤子

public void addShutdownHook

(Thread hook)

Java 虛擬機器會為了響應以下兩類事件而關閉:

1程式正常退出,這發生在最後的非守護執行緒退出時,或者在呼叫 exit(等同於 System.exit)方法時。

2為響應使用者中斷而終止 虛擬機器,如鍵入 ^C;或發生系統事件,比如使用者登出或系統關閉。

關閉鉤子 只是一個已初始化但尚未啟動的執行緒。虛擬機器開始啟用其關閉序列時,它會以某種未指定的順序啟動所有已註冊的關閉鉤子,並讓它們同時執行。執行完所有的鉤子後,如果已啟用退出終結,那麼虛擬機器接著會執行所有未呼叫的終結方法。最後,虛擬機器會暫停。注意,關閉序列期間會繼續執行守護執行緒,如果通過呼叫 exit 方法來發起關閉序列,那麼也會繼續執行非守護執行緒。

一旦開始了關閉序列,則只能通過呼叫 halt 方法來停止這個序列,此方法可強行終止虛擬機器。

一旦開始了關閉序列,則不可能註冊新的關閉鉤子或取消註冊先前已註冊的鉤子。嘗試執行這些操作會導致丟擲 IllegalStateException。

關閉鉤子可在虛擬機器生命週期中的特定時間執行,因此應保護性地對其進行編碼。特別是應將關閉鉤子編寫為執行緒安全的,並儘可能地避免死鎖。關閉鉤子還應該不盲目地依靠某些服務,這些服務可能已註冊了自己的關閉鉤子,所以其本身可能正處於關閉程序中。例如,試圖使用其他基於執行緒的服務(如 AWT 事件指派執行緒)可能導致死鎖。

關閉鉤子應該快速地完成其工作。當程式呼叫 exit 時,虛擬機器應該迅速地關閉並退出。由於使用者登出或系統關閉而終止虛擬機器時,底層的作業系統可能只允許在固定的時間內關閉並退出。因此在關閉鉤子中嘗試進行任何使用者互動或執行長時間的計算都是不明智的 

 3,為您的Java應用程式新增退出事件處理addShutdownHook

一個完整的Java應用程式,通常至少要有一個應用程式的結束點。對於一般程式來說,系統開發者根據需要和個人的偏好,會在程式結束位置,通過新增System.exit(0),或System.out(-1),來結束程式,或不加這些指令,讓程式自然執行到結束。

對於簡單的應用系統,我們直接可以在System.exit(0)程式碼執行前,新增需要在應用程式退出前需要完成的工作,如:關閉網路連線,關閉資料庫連線等。

然而,對於比較複雜的多執行緒應用,執行緒執行的狀態較複雜,我們就很難預料程式何時結束,如何能在應用程式結束事件到來時,處理我們要做的工作呢?這就用到了Java對應用程式的退出的事件出處理機制。

對當前應用程式物件的獲得,Java通過Runtime靜態方法:Runtime.getRuntime()通過Runtime的 void addShutdownHook(Thread hook) 法向Java虛擬機器註冊一個shutdown鉤子事件,這樣一旦程式結束事件到來時,就執行執行緒hook,我們在實際應用時候,只要將程式需要完成之前做的一些工作直接通過執行緒hook來完成。具體演示程式碼如下:

本程式僅演示,如何在Java應用程式中新增系統退出事件處理機制

import java.util.*;
import java.io.*;

public class Untitled1 {
    public Untitled1() {
        doShutDownWork();
    }
   
    private void doShutDownWork() {
        Runtime.getRuntime().addShutdownHook(new Thread() { 
             public void run() {
                   try {
                        FileWriter fw = new FileWriter("d://t.log");
                        System.out.println("I'm going to end");
                        fw.write("the application ended! " + (new Date()).toString());

                        fw.close();
                  } catch (IOException ex) { }
            }
       });
    }
   
    public static void main(String[] args) {
         Untitled1 untitled11 = new Untitled1();
         long s = System.currentTimeMillis();
         for (int i = 0; i < 1000000000; i++) { //在這裡增添您需要處理程式碼 }
                long se = System.currentTimeMillis();
                System.out.println(se - s);
          }
     }
}


在上述程式中,我們可以看到通過在程式中增加Runtime.getRuntime().addShutdownHook(new Thread()) 事件監聽,捕獲系統退出訊息到來,然後,執行我們所需要完成工作,從而使我們的程式更健壯!Java addShutdownHook ---程式出錯退出終極處理辦法 

在<Java多執行緒設計模式>裡提到有關終止處理的方法addShutdownHook().這個方法會在Java執行環境全部結束時 (呼叫System.exit方法時,或者所有非守護執行緒都結束時),呼叫指定執行緒的start方法(這時候該執行緒稱為shutdown hook),使用該方法可以編寫整個程式的終止處理.
    簡單示例:
Java程式碼
public class Main2 {   
 
    public static void main(String[] args) {   
        System.out.println("main: BEGIN");   
           
        // set shutdown hook   
        Runtime.getRuntime().addShutdownHook(   
            new Thread() {   
                public void run() {   
                    System.out.println(Thread.currentThread().getName() + ": Shutdown hook.");   
                }   
        });   
           
        System.out.println("main: SLEEP..");   
           
        // force quit after 3 sec.   
        try {   
            Thread.sleep(3000);   
        } catch (InterruptedException e) {   
            // TODO: handle exception   
        }   
           
        System.out.println("main: EXIT");   
        // force quit   
//      System.exit(0);   
        Runtime.getRuntime().exit(0);   
 
        // unreachable code   
        System.out.println("main: END");   
    }   
}  

    關於守護執行緒:
    Java有兩種Thread:“守護執行緒Daemon”與“使用者執行緒User”。
    我們之前看到的例子都是使用者,守護執行緒是一種“在後臺提供通用性支援”的執行緒,它並不屬於程式本體。
    從字面上我們很容易將守護執行緒理解成是由虛擬機器(virtual machine)在內部建立的,而使用者執行緒則是自己所建立的。事實並不是這樣,任何執行緒都可以是“守護執行緒Daemon”或“使用者執行緒User”。他們在幾乎每個方面都是相同的,唯一的區別是判斷虛擬機器何時離開:
    使用者執行緒:Java虛擬機器在它所有非守護執行緒已經離開後自動離開。
    守護執行緒:守護執行緒則是用來服務使用者執行緒的,如果沒有其他使用者執行緒在執行,那麼就沒有可服務物件,也就沒有理由繼續下去。
    setDaemon(boolean on)方法可以方便的設定執行緒的Daemon模式,true為Daemon模式,false為User模式。setDaemon(boolean on)方法必須線上程啟動之前呼叫,當執行緒正在執行時呼叫會產生異常。isDaemon方法將測試該執行緒是否為守護執行緒。值得一提的是,當你在一個守護執行緒中產生了其他執行緒,那麼這些新產生的執行緒不用設定Daemon屬性,都將是守護執行緒,使用者執行緒同樣。
    例:我們所熟悉的Java垃圾回收執行緒就是一個典型的守護執行緒,當我們的程式中不再有任何執行中的Thread,程式就不會再產生垃圾,垃圾回收器也就無事可做,所以當垃圾回收執行緒是Java虛擬機器上僅剩的執行緒時,Java虛擬機器會自動離開。
    守護執行緒是為其他執行緒的執行提供便利的執行緒。守護執行緒不會阻止程式的終止。
    非守護執行緒包括常規的使用者執行緒或諸如用於處理GUI事件的事件排程執行緒。
    程式可以包含守護執行緒和非守護執行緒。
    當程式只有守護執行緒時,該程式便可以結束執行。
    如果要使一個執行緒成為守護執行緒,則必須在呼叫它的start方法之前進行設定(通過以true作為引數呼叫執行緒的setDaemon方法,可以將該執行緒定義為一個守護執行緒),否則會丟擲IllegalThreadStateException異常。如果執行緒是守護執行緒,則isDaemon方法返回真。
    注:1、如果線上程已經啟動後,再試圖使該執行緒成為守護執行緒,則會導致IllegalThreadStateException異常。
     2、事件排程執行緒是一個無窮迴圈的執行緒,而不是守護執行緒。因而,在基於視窗的應用程式呼叫System類的exit方法之前,事件排程執行緒不會終止。
     3、不能將關鍵任務分配給守護執行緒。這些任務將會在事先沒有警告的情況下終止,這可能導致不能正確地完成它們