以前在開發時只知道依靠資料庫事務來保證程式關閉時資料的完整性。
但有些時候一個業務上要求的原子操作,不一定只包括資料庫,比如外部介面或者訊息佇列。此時資料庫事務就無能為力了。
這時我們可以依靠java提供的一個工具方法:java.lang.Runtime.addShutdownHook(Thread hook)
addShutdownHook方法可以加入一個鉤子,在程式退出時觸發該鉤子。
(退出是指ctrl+c或者kill -15,但如果用kill -9 那是沒辦法的,具體有關kill的signal機制有篇大牛的文章《Linux 訊號signal處理機制》)
鉤子做什麼操作都可以,甚至可以迴圈檢查某個執行緒的狀態,直到業務執行緒正常退出,再結束鉤子程式就可以保證業務執行緒的完整性
例子程式如下:
例項程式在執行過程中按下ctrl -c或者 kill -15,由於鉤子程式的迴圈檢測,能夠保證執行緒執行完畢後,程式才關閉。
1 /** 2 * Created by IntelliJ IDEA. 3 * User: Luo 4 * Date: 13-7-11 5 * Time: 下午3:12 6 */ 7 8 public class TestShutdownHook { 9 10 /** 11 * 測試執行緒,用於模擬一個原子操作 12 */ 13 private static class TaskThread extends Thread { 14 @Override 15 public void run() { 16 System.out.println("thread begin ..."); 17 TestShutdownHook.sleep(1000); 18 System.out.println("task 1 ok ..."); 19 TestShutdownHook.sleep(1000); 20 System.out.println("task 2 ok ..."); 21 TestShutdownHook.sleep(1000); 22 System.out.println("task 3 ok ..."); 23 TestShutdownHook.sleep(1000); 24 System.out.println("task 4 ok ..."); 25 TestShutdownHook.sleep(1000); 26 System.out.println("task 5 ok ..."); 27 28 System.out.println("thread end\n\n"); 29 } 30 } 31 32 /** 33 * 註冊hook程式,保證執行緒能夠完整執行 34 * @param checkThread 35 */ 36 private static void addShutdownHook(final Thread checkThread) { 37 //為了保證TaskThread不在中途退出,新增ShutdownHook 38 Runtime.getRuntime().addShutdownHook(new Thread() { 39 public void run() { 40 System.out.println("收到關閉訊號,hook起動,開始檢測執行緒狀態 ..."); 41 //不斷檢測一次執行狀態,如果執行緒一直沒有執行完畢,超時後,放棄等待 \ 42 for (int i = 0; i < 100; i++) { 43 if (checkThread.getState() == State.TERMINATED) { 44 System.out.println("檢測到執行緒執行完畢,退出hook"); 45 return; 46 } 47 TestShutdownHook.sleep(100); 48 } 49 System.out.println("檢測超時,放棄等待,退出hook,此時執行緒會被強制關閉"); 50 } 51 }); 52 } 53 54 55 private static void sleep(long millis) { 56 try { 57 Thread.sleep(millis); 58 } catch (InterruptedException e) { 59 e.printStackTrace(); 60 } 61 } 62 63 public static void main(String[] args) throws InterruptedException { 64 final TaskThread taskThread = new TaskThread(); 65 //為了保證TaskThread不在中途退出,新增ShutdownHook 66 addShutdownHook(taskThread); 67 //執行TaskThread 68 taskThread.start(); 69 } 70 71 }