Java虚拟机的退出规则 :JVM在所有非守护线程都结束后才会退出。
-
普通程序:
main线程执行完最后一行,main线程结束 → JVM没有其他非守护线程了 → JVM退出(平时可以写写测试代码) -
服务器程序:
server.start()启动了一些后台非守护线程(负责监听端口、处理请求),这些线程会一直运行,等待请求;(启动服务的场景)
所以tomcat,jetty这些都是会通过脚本启功一个非守护线程来保持服务的运行的。
来看看tomcat如何优雅的启动项目吧
// Tomcat 的简化版启动代码
public class Bootstrap {
public static void main(String[] args) {
// 1. 初始化 Catalina(Tomcat的核心容器)
Catalina catalina = new Catalina();
// 2. 加载配置文件(server.xml)
catalina.load();
// 3. 启动!------没错,这里也是start()
catalina.start();
// 4. 重点来了:这里会调用await()方法
catalina.await();
// 5. 只有在收到关闭命令后,才会执行到这里
catalina.stop();
}
}
为什么要await一下呢?就是为了更优雅
server.start(); // 启动后台线程处理请求
server.await(); // ★ main线程在这里等待,不会结束
// 直到收到 shutdown 命令,await() 才会返回
server.stop(); // 然后执行清理工作
不同启动方式的对比
| 特性 | 原生 HttpServer | Tomcat |
|---|---|---|
| main线程状态 | 直接结束 | 在 await() 处阻塞等待 |
| 后台线程 | 存在,处理请求 | 存在,处理请求 |
| 如何停止 | 只能强制kill进程 | 可以通过shutdown命令优雅停止 |
| 控制台表现 | 看似"挂起"在那里 | 看似"挂起"在那里 |
| 关闭钩子 | 难以及时执行清理 | 可以执行清理(如保存Session) |
-
更好的控制:main线程保持运行,可以随时响应关闭命令
-
优雅关闭:收到关闭命令后,可以等待正在处理的请求完成
-
清理资源:有机会保存Session、关闭连接池等