SpringBoot项目启动失败进程不结束问题
目录
1. 问题背景
2.问题分析
3.排查
3.1.查看线程堆栈
3.2.查看阻塞线程信息
3.3.定位到异常线程
3.4.定位到具体业务代码
3.5.解决
4.疑问
1. 问题背景
一个spring boot开发的项目,spring boot版本是2.7.13。其他同事定义的bean冲突了,导致项目启动失败,进程也不退出。
我这边是因为线程池问题导致的。
2.问题分析
正常来说,项目启动失败后,进程会直接退出。所以问题大概率是死锁、长时间等待外部资源(例如数据库连接、I/O操作)、线程池未关闭。
3.排查
3.1.查看线程堆栈
IDEA内置了一个工具,可以直接查看当前所有线程的堆栈信息,相当于jstack <pid> 命令。
3.2.查看阻塞线程信息
一个一个点击红色图标的线程,查看线程的具体情况。
标识有daemon的线程不需要过多关注,daemon
是一个线程的属性,它表示该线程是 守护线程。守护线程与普通线程的区别在于,当所有非守护线程(即普通线程)结束时,守护线程会自动结束,无论它是否执行完任务。
3.3.定位到异常线程
通过一个一个点击,我这边除了pool-15-thread是普通线程,其他都是守护线程,重点关注这个。
通过堆栈记录,基本上可以确定是因为线程池一直在执行导致的启动失败进程没有结束问题。
然后我在idea里全局搜索线程池相关的类ThreadPoolExecutor,发现项目中还是挺多的,无法确定具体是那个。堆栈记录也没有更详细的信息。
没办法,只能通过打断点了。
outerTask是正在执行的实际任务,通过这个可以看到具体的业务代码。
3.4.定位到具体业务代码
在这里发现创建了一个定时执行的线程,所以,启动失败该线程也不会结束。
3.5.解决
添加一个销毁钩子,spring容器删除的时候,该钩子就会被调用。在这个方法里,我们来手动关闭一下线程池。
4.疑问
可能大家会有疑问,我们项目也是这样使用的,并没有出现这种问题。
springboot 加载bean是有顺序的,我这边是先创建的线程,然后在加载其他bean的时候报错了。如果你们是先报的错,后面就不会创建线程池了,自然就没有这种问题。