以前在開發時只知道依靠數據庫事務來保證程序關閉時數據的完整性。
但有些時候一個業務上要求的原子操作,不一定只包括數據庫,比如外部接口或者消息隊列。此時數據庫事務就無能為力了。
這時我們可以依靠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 }