什么叫做优雅停机:
通俗点理解(以tomcat为例),优雅停机就是当tomcat收到停机命令时,tomcat会关闭所有入口(表明我已经要停机了,你们别再来请求我了),同时对已经接受的请求继续完成相应的处理逻辑。当所有的用户定义的线程都处理完成,进程内只剩下daemon线程时,机彻底关机,杀死进程。
优雅停机的正确姿势:
ps -ef|grep java 找到你需要杀死的pid
假如我们要杀死的进程pid为254569,则执行 kill 254569即可。切记千万不能如此操作 kill -9 254569,如此操作后进程马上杀死,同时会造成已经接收的请求不会继续处理,从而造成一些不必要的逻辑错误。
问题回顾
在系统逻辑实现时,为了提高系统吞吐量,程序猿们往往会使用一些多线程技术,来处理一些延时高的、强依赖资源的一些请求(消耗资源比较高的通常是DBio,网络io,文件读写io)。但是在使用时经常忽略了线程的关闭问题,下面就是腾讯王卡在运营过程中发现的问题。
在本地项目停止过程中发现,tomcat在停止时很耗时,甚至停止过程中直接抛出异常,这个现象引起了大家的注意。
我们是这样定位的:
第一:停止tomcat
第二:使用jstack命令查看停机后,还有那些线程在运行
第三:分析打印出来的堆栈信息
在分析jstack打印出来的堆栈信息时,找出和项目相关代码,查看可疑点。
问题分析:
该代码是向线程池提交执行一个逻辑,该逻辑是一个死循环
改进后的代码如:
改进后的方法,没使用线程池提交,直接new一下thread执行即可。那问题来了,那线程池和直接new一个thread执行相同的方法,为啥线程池就不可以,new 一个thread就可以呢?
带着问题,我们分别查看线程的shutdown方法和线程的interrupt。相关代码截图如下
我们先分析一下线程池的shutdown方法。
checkShutdownAccess检查操作权限
advanceRunState关闭线程池
interruptIdleWorkers中断空闲线程
onShutdown取消一些延迟任务
大家请注意interruptIdleWorkers的作用中断空闲线程,而我们的方法是一个死循环,自然不能中断
线程池里面有个方法叫shutdownNow,里面有个方法interruptWorkers,该方法是强制关闭所有线程,不论是空闲还是繁忙。到此我们了解了线程池为什么清理死循环的线程了。
而直接new一个thread之所以可以中断一个死循环的线程,是因为interrupt0这个方法的存在,该方法作用是给线程打个标记(Just to set the interrupt flag),告诉操作系统,该线程可以终止,操作系统会自动中断被标记中断的线程。
问题解方案
最后建议大家,如果确实有业务需要写一个死循环,不中断的处理一些业务,我们建议使用如下两种写法
写法一:
该方法在bean被销毁时,start标志会置为false,从而退出死循环
写法二:
使用interrupted()方法,根据线程的中断状态来退出循环
总而言之,言而总之:线程池不能用于处理死循环逻辑。
如分析有误,欢迎大家拍砖
作者也玩公众号,欢迎关注《JAVA之庖丁解牛》