这篇文章主要介绍“在Spring环境中怎么正确关闭线程池”,在日常操作中,相信很多人在在Spring环境中怎么正确关闭线程池问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”在Spring环境中怎么正确关闭线程池”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!在这一节,先不讨论应用中线程池该如何优雅关闭以达到优雅停机的效果,只是简单介绍一下线程池正确关闭的姿势为简化讨论的复杂性,本文的线程池均是指JDK中的java.util.concurrent.ThreadPoolExecutor正确关闭线程池的关键是 shutdown
+ awaitTermination
或者 shutdownNow
+ awaitTermination
一种可能的使用姿势如下:一个任务会有如下几个状态:未提交,此时可以将任务提交到线程池已提交未执行,此时任务已在线程池的队列中,等待着执行执行中,此时任务正在执行执行完毕那么,执行shutdown
方法或shutdownNow
方法之后,将会影响任务的状态shutdown拒绝新任务提交待执行的任务不会取消正在执行的任务也不会取消,将继续执行shutdownNow拒绝新任务提交取消待执行的任务尝试取消执行中的任务(仅仅是做尝试,成功与否取决于是否响应InterruptedException,以及对其做出的反应)接下来看一下java doc对这两个方法的描述:shutdown: Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down.
This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.shutdownNow: Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.
This method does not wait for actively executing tasks to terminate. Use awaitTermination to do that.
There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt, so any task that fails to respond to interrupts may never terminate.Java doc 提到,这两个方法都不会等执任务执行完毕,如果需要等待,请使用awaitTermination
。该方法带有超时参数:如果超时后任务仍然未执行完毕,也不再等待。毕竟应用总归要停机重启,而不可能无限等待下去,因此超时机制是提供给用户的最后一道底线综上,shutdown(Now) + awaitTermination 确实是实现线程池优雅关闭的关键这一节内容其实才是本文要介绍的重心。上一小节内容我们知道了如何优雅关闭线程池,但那是一般意义上方法论指导,如果将线程池运用于我们的应用中,譬如Spring Boot环境中,复杂度将会变得不一样本一节,将会介绍线程池在Spring (Boot)环境中优雅关闭遇到的一个问题跟挑战,以及解决方案注:本节使用Spring Boot举例,仅仅是因为它的应用面广,受众多,大家容易理解,并不代表只在该环境下才会出问题。在纯Spring、甚至非Spring环境,都有可能出现问题场景1我们来假设一个场景,有了场景的铺垫,对问题的理解会简单一些自定义线程池,用于异步任务的执行。此处为演示方便使用Executors.newFixedThreadPool(1)
生成了只有一个线程的线程池高并发请求/incr接口,每次请求该接口,都会往线程池中添加一个任务,任务异步执行的过程中依赖Redis此时,要求停机发布新版本,按照Java System#exit 无法退出程序的问题文章,我们知道了优雅停机的一般步骤:切断上游流量入口,确保不再有流量进入到当前节点向应用发送kill 命令,在设定的时间内待应用正常关闭,若超时后应用仍然存活,则使用kill -9命令强制关闭当JVM接收到kill命令,会唤起应用中所有的Shutdown Hooks,等待Shutdown Hooks执行完毕便可以正常关机;与此同时,应用会接着处理在途请求,以确保不会向客户端抛出连接中断异常,实现无感知发布一切看起来很美好,然而…当JVM收到kill指令后,便会唤醒所有的Shutdown Hook,而其中有一个Shutdown Hook是Spring应用在启动之初注册的,它的作用是对Spring管理的Bean进行回收,并销毁IOC容器那么问题就产生了:以我们的场景为例,线程池里的任务与Spring Shutdhwon Hook正在并发地执行着,一旦任务执行期依赖的资源先行被释放,那任务执行时必然会报错在我们的场景中,就很有可能因为Redis连接被回收,从而导致redisTemplate.opsForValue().increment("demo", 1L);
抛出异常,执行失败如图示:Jedis连接池先行被回收下一刻,线程池里的任务尝试获取Jedis连接,失败并抛出异常场景2除了上述场景外,还有一个场景或许大家也经常会碰到:本地启动一个定时任务,按一定频率将数据从DB加载到Cache中例如:每100ms向线程池里扔一个任务任务是:从DB中取出数据,放入缓存(例如Local Cache,Redis)在Spring Shutdown Hook执行期间,新的任务仍然会产生,又或者旧的任务未执行完毕,一旦尝试获取DB资源,就可能由于资源被回收而获取失败,抛出异常此时的系统关闭已经不优雅—任务执行有异常,这种异常可能对业务有损,我们应尽量避免类似问题的产生,而不是抱着”算了吧,反正产生这个问题的概率很低”,或者”算了吧,反正异常对我目前业务影响也不大”的态度,这是技术人的基本修养,也是对自我提高的要求—目前业务影响不大,允许不优先解决,但是期望掌握一种解决方案,将来有一天如果碰到了对业务损伤比较大的场景,可以很有底气地说:我能行这个问题产生的根因,是Spring Shutdown Hook与线程池里的任务并发执行,有可能使任务依赖的资源被提前回收导致的。那么一个很直白的思路即是:在切断流量之后,能否让线程池先关闭,再执行Spring 的Shutdown Hook,避免依赖资源被提前回收?顺着这个思路,有三个问题需要解决:线程池如何关闭线程池如何感知Spring Shutdown Hook将要被执行如何让线程池先于Spring Shutdown Hook关闭对于第一个问题,本文的上一个小节线程池正确关闭的姿势已经给出了解决方案:即shutdown(Now) + awaitTermination
对于第二个问题,Spring Shutdown Hook被触发的时候,会主动发出一些事件,我们只要监听这些的事件,就能够做出相应的反应对于第三个问题,我们只要在这些事件的监听器中先行将线程池关闭,再让程序走接下来的关闭流程即可二、三涉及到Spring 的Shutdown Hook 执行过程,具体原理本篇按下不表,留待下一篇进行分析从上图中可以看出,只要在destroyBeans
之前关闭线程池即可,因此,有两种解决方案:监听Spring的ContextClosedEvent事件,在事件被触免费云主机域名发时关闭线程池实现Lifecycle接口,并在其stop方法中关闭线程池此处以监听ContextClosedEvent
为例:此处大家或许能看出一些小问题:需要自行管理线程池。在Spring环境中,我们其实有更多的选择:使用Spring提供的org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
,并将实例交给Spring管理代码如下:注: ThreadPoolTaskExecutor的waitForTasksToCompleteOnShutdown
+ awaitTerminationSeconds
等于ThreadPoolExecutor的shutdown
+ awaitTermination
,且在定义线程池时就将优雅关闭行为一同定义完毕,实现了高内聚的目的在Spring中使用ThreadPoolTaskExecutor,更便捷:不用再自行管理线程池,获取的时候也很方便,直接注入即可在需要关闭的时候,直接调用destroy方法即可实现优雅关闭这样,Spring就会等到线程池关闭(超时)后,才会接着往下执行Bean的销毁、资源回收、应用上下文关闭的逻辑,确保被依赖资源不会被提前回收掉到此,关于“在Spring环境中怎么正确关闭线程池”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注百云主机网站,小编会继续努力为大家带来更多实用的文章!
本篇内容主要讲解“C++如何实现即时通信”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大免费云主机域名家学习“C++如何实现即时通信”吧!多次点击TcpClient.exe可以产生多个窗口运行效果:到此,相信大家对“C++如…
免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。