【Runtime.addShutdownHook 和 Spring优雅关闭之:registerShutdownHook】

转载自【Spring优雅关闭之:ShutDownHook】:https://blog.csdn.net/qq_26323323/article/details/89814410

程序在异常结束时,可能来不及释放一些资源,可以通过预埋钩子,当异常退出时进行捕获:

Runtime.getRuntime().addShutdownHook();
我们模拟一个异常,在程序报错之后,钩子函数会得到执行。可以通过使用Runtime添加钩子函数来做到优雅停机。

  public static void main(String[] args) throws Exception {

        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("正在关闭一些连接资源...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ignore) {
                }
            }
        }));
        
        // 模拟执行业务耗时
        int i = 0;
        while (true) {
            System.out.println("i am working...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ignore) {
            }

            // 模拟一个异常
            if (i++ > 5) {
                throw new Exception("error.");
            }
        }
    }

注:这个钩子对kill(kill -15)有效,对kill -9 还是无效的。 (所以在生产不得已要用kill时,也一般先用kill ,尽量不用kill -9)

适用的场景:

  • 程序正常退出

  • 使用System.exit()

  • 终端使用Ctrl+C触发的中断

  • 系统关闭

  • OutofMemory宕机

  • 使用Kill pid杀死进程(使用kill -9是不会被调用的)

Spring如何添加钩子函数,实现优雅关闭

ApplicationContext.registerShutdownHook();

ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootNonWebApplication.class, args);

applicationContext.registerShutdownHook();
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    // 注册钩子
applicationContext.registerShutdownHook();

// 通过源码可以看到

@Override
public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        // No shutdown hook registered yet.
        this.shutdownHook = new Thread() {
            @Override
            public void run() {
                synchronized (startupShutdownMonitor) {
                    doClose();
                }
            }
        };
        // 也是通过这种方式来添加
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}

重点是这个doClose()方法:

protected void doClose() {
    // Check whether an actual close attempt is necessary...
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        if (logger.isInfoEnabled()) {
            logger.info("Closing " + this);
        }
 
        LiveBeansView.unregisterApplicationContext(this);
 
        try {
            // Publish shutdown event.
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {
            logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
        }
 
        // Stop all Lifecycle beans, to avoid delays during individual destruction.
        if (this.lifecycleProcessor != null) {
            try {
                this.lifecycleProcessor.onClose();
            }
            catch (Throwable ex) {
                logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
            }
        }
 
        // Destroy all cached singletons in the context's BeanFactory.
        destroyBeans();
 
        // Close the state of this context itself.
        closeBeanFactory();
 
        // Let subclasses do some final clean-up if they wish...
        onClose();
 
        // Switch to inactive.
        this.active.set(false);
    }
}

doClose()方法会执行bean的destroy(),也会执行SmartLifeCycle的stop()方法,我们就可以通过重写这些方法,借助对象的生命周期的管理,实现平滑shutdown。

结合这一篇:
Spring——定义Bean init Method与destroy Method的三种方式

如当我们创建一个bean时,可以指定initMethod 和 destroyMethod:

@Configuration
public class XxDataSourceConfiguration {

    @Bean(initMethod = "myInitMethod", destroyMethod = "myDestroyMethod")
    public XxDataSource getBean(){
        return new XxDataSource();
    }
}

class XxDataSource {

    public void myInitMethod() {
        System.out.println("我是一个init方法...");
    }


    public void myDestroyMethod() {
        System.out.println("我是一个destroy方法...");
    }
}

【Spring】Lifecycle的使用与源码分析
LifeCycle接口定义了Spring容器的生命周期,任何被Spring管理的对象都可以实现该接口。当Spring容器本身启动和停止时,会回调LifeCycle接口中定义的方法【start()、stop()】。

// 1)不注册,只创建容器,结果如下:
start // 只输出start,说明只执行了LifeCycle.start()方法
    
// 2)注册钩子
start
stop(Runnable) // 执行了LifeCycle.stop()方法
我们当前讨论的都是Spring非Web程序,如果是Web程序的话,不需要我们来注册钩子函数,Spring的Web程序已经有了相关的代码实现优雅关闭了。

SpringBoot项目也会在异常时先执行destroyMethod方法。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用和介绍了使用Java中的Runtime.getRuntime().addShutdownHook()方法来添加关闭钩子。关闭钩子是一个已经初始化但尚未启动的线程,当JVM关闭时会执行这些钩子。关闭钩子可以在JVM关闭时执行一些内存清理和资源回收等工作。引用中也提到了这个方法,并指出当程序正常退出、系统调用System.exit方法或虚拟机被关闭时,才会执行添加的shutdownHook线程。 因此,使用Runtime.getRuntime().addShutdownHook()方法可以在Java程序关闭时执行一些必要的清理工作,以确保程序的正常退出和资源的释放。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [thrift服务端和客户端实现Nifty.zip](https://download.csdn.net/download/weixin_39841856/11384893)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [高并发编程-Runtime.getRuntime().addShutdownHook为自己的应用添加hook](https://blog.csdn.net/yangshangwei/article/details/102583944)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [runtime.getruntime().addshutdownhook](https://blog.csdn.net/Iccmo/article/details/71107697)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值