当点击tomcat 的shutdown.bat 关闭服务器时, 发现黑窗口并没有关闭完。上面提示有其他线程仍然在运行
守护线程与非守护线程
- 守护线程: 当主线程关闭后,守护线程也会跟着关闭,如jvm中GC线程就是守护线程
- 非守护线程: 不会随着主线程的关闭而关闭。
分析
我的web项目是采用tomcat, 用的是SSM架构, 在项目启动后,会开辟线程池进行处理业务。因此当tomcat关闭的时候,因为线程池默认不是守护线程,因此整个进程不会停掉。
需要在关闭tomcat的时候,先关闭线程池。
线程池的关闭
其中接口ExecutorService 提供关闭线程池的抽象方法。
以下是具体实现
shutdown
/*
线程池状态变为 SHUTDOWN
- 不会接收新任务
- 但已提交任务会执行完
- 此方法不会阻塞调用线程的执行
*/
void shutdown();
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改线程池状态
advanceRunState(SHUTDOWN);
// 仅会打断空闲线程
interruptIdleWorkers();
onShutdown(); // 扩展点 ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 尝试终结(没有运行的线程可以立刻终结,如果还有运行的线程也不会等)
tryTerminate();
}
shutdownNow
/*
线程池状态变为 STOP
- 不会接收新任务
- 会将队列中的任务返回
- 并用 interrupt 的方式中断正在执行的任务
*/
List<Runnable> shutdownNow();
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改线程池状态
advanceRunState(STOP);
// 打断所有线程
interruptWorkers();
// 获取队列中剩余任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 尝试终结
tryTerminate();
return tasks;
}
其它方法
// 不在 RUNNING 状态的线程池,此方法就返回 true
boolean isShutdown();
// 线程池状态是否是 TERMINATED
boolean isTerminated();
// 调用 shutdown 后,由于调用线程并不会等待所有任务运行结束,因此如果它想在线程池 TERMINATED 后做些事情,可以利用此方法等待
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
实际解决
因为我的线程池是一个spring组件, 实现了InitializingBean, DisposableBean接口,因此在bean初始化的时候回调进行线程池的初始化,在tomcat容器销毁(这个时候spring组件也会跟着销毁)的时候,调用回调,进行线程池的关闭。