注冊鈎子函數
private static void shutDownThreadPool(){
Runtime.getRuntime().addShutdownHook( new ShutdownHookThread(log,() -> {
poolExecuteConcurrentHashMap.forEach( (k,v) -> {
if(Optional.ofNullable( v ).isPresent())
v.shutdown();
} );
return null;
}) );
}
public class ShutdownHookThread extends Thread {
private volatile boolean hasShutdown = false;
private AtomicInteger shutdownTimes = new AtomicInteger(0);
private final Logger log;
private final Callable callback;
public ShutdownHookThread(Logger log, Callable callback) {
super("ShutdownHook");
this.log = log;
this.callback = callback;
}
@Override
public void run() {
synchronized (this) {
log.info("shutdown hook was invoked, " + this.shutdownTimes.incrementAndGet() + " times.");
if (!this.hasShutdown) {
this.hasShutdown = true;
long beginTime = System.currentTimeMillis();
try {
this.callback.call();
} catch (Exception e) {
log.error("shutdown hook callback invoked failure.", e);
}
long consumingTimeTotal = System.currentTimeMillis() - beginTime;
log.info("shutdown hook done, consuming time total(ms): " + consumingTimeTotal);
}
}
}
}
對于通過注冊ShutdownHook實作的優雅退出,需要注意如下幾點,防止踩坑
(1)ShutdownHook在某些情況下并不會被執行,例如JVM崩潰、無法接收信号量和kill-9 pid等。
(2)當存在多個ShutdownHook時,JVM無法保證它們的執行先後順序。
(3)在JVM關閉期間不能動态添加或者去除ShutdownHook。
(4)不能在ShutdownHook中調用System.exit(),它會卡住JVM,導緻程序無法退出。
信号量回調函數
Signal signal = new Signal( System.getProperty( "os.name" ).toLowerCase().startsWith( "win" ) ? "INT" : "TERM" );
Signal.handle( signal,signal1 -> {
System.out.println("釋放資源");
} );
其中Signal構造函數的參數為String字元串,它代表了作業系統支援的信号量清單(此處注意:不同作業系統支援的信号量不同),如表1-1所示為Linux支援的一些常用信号量。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL90TQOdXSE1ENnR1Y0R2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyAjM0AjM1IjMyADNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
判斷是否是Windows作業系統,如果是則選擇SIGINT,接收Ctrl+C中斷的指令,否則選擇TERM信号,接收SIGTERM(等價于kill pid)指令(備注:這裡僅是支援Windows和Linux作業系統的代碼示例)。
将執行個體化之後的SignalHandler注冊到JDK的Signal,一旦Java程序接收到killpid或Ctrl+C,則回調handle接口:
對于采用注冊 SignalHandler 實作優雅退出的程式,在 handle 接口中一定要避免阻塞操作,否則它會導緻已經注冊的 ShutdownHook無法執行,系統也無法退出