面试官:介绍一下 Tomcat 的 IO 模型?

👉 这是一个或许对你有用 的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:

👉这是一个或许对你有用的开源项目

国产Star破10w的开源项目,前端包括管理后台、微信小程序,后端支持单体、微服务架构

RBAC权限、数据权限、SaaS多租户、商城 、支付、工作流、大屏报表、ERP、CRMAI大模型、IoT物联网等功能:

【国内首批】支持 JDK17/21+SpringBoot3、JDK8/11+Spring Boot2双版本


一句话结论

Tomcat 支持四种 IO 模型:BIO、NIO、NIO2、APR。从 Tomcat 8 开始,默认是 NIO,99% 的场景不用改。 面试时能把这四种的原理、差异、适用场景说清楚,就算过关。

这道题真正考的不是背名字,而是你对网络通信模型、Tomcat 架构演进、以及性能调优的理解。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

BIO:一个连接一个线程,死等

Tomcat 7 及以前的默认模型。

工作方式像"一对一服务":Acceptor 接收到连接后,为每个 Socket 分配一个工作线程。这个线程从读请求到写响应全程占用,中间网络慢就干等着,线程被阻塞,CPU 却在空转。

问题很明显: 线程池满了就拒绝新连接。并发能力完全受限于线程数。要支撑 1000 个并发连接,就得开 1000 个线程,内存和上下文切换费用大得离谱。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

NIO:多路复用,一个线程盯一堆连接

Tomcat 8+ 的默认模型,也是目前的主流选择。

核心是 多路复用器 Selector 。少量的 Poller 线程通过 Selector 监听大量 Channel,只有某个 Channel 真正有 I/O 事件(读就绪、写就绪)时,线程才去处理。没事做的连接不占线程,这就是性能飞跃的根本原因。

打个比方:BIO 是每个客人配一个服务员的高端覿店,NIO 是一个服务员同时看 20 张桌子的快餐店------谁举手服务谁,不举手的不浪费人力。

APR:绕过 JVM,直接跟内核对话

通过 JNI 调用本地库,绕过 Java NIO 层,直接和操作系统内核交互。

它利用了 epoll(Linux)、kqueue(BSD)等高效事件通知机制,内存管理也绕过了 JVM 堆,支持零拷贝。理论上这是性能天花板。

但明显的代价是部署复杂度。 需要安装 APR 本地库,依赖操作系统环境,Docker 化部署也得额外处理。它的优势主要体现在静态资源处理 (图片、大文件下载)和底层网络操作上,对纯动态内容的提升并不明显。

NIO2:听起来很美,用得很少

NIO2 即 AIO(异步 I/O),理论上比 NIO 更进一步------不用你主动轮询,操作系统完成 I/O 后直接回调通知你。

但现实很骨感: Linux 对真正的 AIO 支持一直不完善(尤其是网络 I/O 场景),NIO 的 epoll 已经足够高效,所以 NIO2 在生产环境里几乎没人用。它更像是一个"技术上存在,工程上无感"的选项。

一张表看懂四种模型的差异

BIO NIO(默认) APR NIO2
实现 纯 Java,同步阻塞 纯 Java,Selector 多路复用 JNI 调用本地库 纯 Java,异步回调
线程模型 1 连接 = 1 线程 少量线程管理大量连接 本地事件驱动 异步回调
并发能力 极高 理论高,实际不确定
部署 简单 简单 复杂(需本地库) 简单
适合场景 低并发老项目 绝大多数 Web 应用 静态资源为主的场景 基本不用

选型结论:默认 NIO 不动,静态资源多考虑 APR,BIO 和 NIO2 忘了就行。

怎么配置:server.xml 里改一行的事

server.xmlConnector 里改 protocol 属性即可:

go 复制代码
<!-- NIO(Tomcat 8+ 默认,不写也是这个) -->
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
           connectionTimeout="20000" redirectPort="8443" />

<!-- NIO2 -->
<Connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
           connectionTimeout="20000" redirectPort="8443" />

<!-- APR(需要先装库) -->
<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol"
           connectionTimeout="20000" redirectPort="8443" />

实际项目中,更常见的调优是调 maxConnectionsmaxThreads 这些参数,而不是换 IO 模型。

三个常见误区,面试少说少失分

误区一:"Tomcat 性能差,高并发得换 Netty"。 对于标准的 HTTP 请求响应式 Web 服务(Spring MVC 应用),Tomcat NIO 的性能完全能打。Netty 的优势在于自定义协议和更精细的网络控制,主要用在 RPC、即时通信等场景。别一上来就拿 Netty 打压 Tomcat。

误区二:"用了 NIO 就不会阻塞了"。 NIO 解决的是 I/O 等待的阻塞 。但你的业务逻辑里有慢 SQL、重计算、长时间的 RPC 调用?工作线程照样被堵。IO 模型解决不了业务层面的性能问题。

误区三:"APR 无论如何都比 NIO 快"。 APR 的优势集中在静态资源和底层网络操作。对于纯动态内容的 Java 应用,提升很小,但部署复杂度确实派上来了。不压测就下结论,都是猎人。


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,"长按 "或"扫描"下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

go 复制代码
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)