Tomcat 之所以能"一直运行",和普通方法执行完就退出不一样,核心原因在于 线程模型和事件循环。
1. 普通方法为什么会结束?
-
一个普通方法调用,执行到最后一行代码,JVM 的执行栈就会弹出这个方法栈帧。
-
如果主线程执行的所有方法都结束了,并且没有其他非守护线程存活,JVM 就会退出。
所以,单纯写一个 main
方法,执行完打印语句就退出了。
2. Tomcat 为什么不会退出?
Tomcat 作为 Web 容器,有一些特点:
-
启动类创建非守护线程
-
Tomcat 的
main
方法会初始化各种组件(如Catalina
、连接器、线程池)。 -
其中会启动非守护线程,比如:
-
Acceptor 线程:监听端口(默认 8080),不断等待客户端请求。
-
工作线程池:处理请求。
-
后台维护线程:session 清理、资源监控。
-
-
只要这些非守护线程存在,JVM 就不会退出。
-
-
事件循环(死循环监听)
-
监听端口的线程通常写成类似:
while (running) { Socket s = serverSocket.accept(); // 分发给工作线程处理 }
-
这就保证线程一直阻塞在等待 I/O 上,不会自然结束。
-
-
生命周期管理
-
Tomcat 的
Server
对象会控制所有组件(start
、await
、stop
)。 -
常见的
server.await()
内部就是一个阻塞等待逻辑(如监听SHUTDOWN
命令)。 -
直到你显式调用
shutdown
,才会释放端口、结束线程池、停止 JVM。
-
3. 对比理解
-
方法调用:执行完 -> 栈帧弹出 -> 若主线程结束,JVM 退出。
-
Tomcat:启动时拉起多个非守护线程,这些线程要么在循环,要么在等待 I/O,所以一直活着,JVM 自然不会退出。
✅ 总结一句话:
Tomcat 一直运行是因为它启动了非守护线程(监听端口 + 工作线程池),这些线程阻塞或循环等待任务,主线程也会在 await()
中阻塞等待关闭信号,因此进程不会像普通方法那样自然结束。