天天看點

Java 最優雅關閉線程池得兩種方式

注冊鈎子函數

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支援的一些常用信号量。

Java 最優雅關閉線程池得兩種方式
Java 最優雅關閉線程池得兩種方式

判斷是否是Windows作業系統,如果是則選擇SIGINT,接收Ctrl+C中斷的指令,否則選擇TERM信号,接收SIGTERM(等價于kill pid)指令(備注:這裡僅是支援Windows和Linux作業系統的代碼示例)。

将執行個體化之後的SignalHandler注冊到JDK的Signal,一旦Java程序接收到killpid或Ctrl+C,則回調handle接口:

Java 最優雅關閉線程池得兩種方式

對于采用注冊 SignalHandler 實作優雅退出的程式,在 handle 接口中一定要避免阻塞操作,否則它會導緻已經注冊的 ShutdownHook無法執行,系統也無法退出

繼續閱讀