⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 小哈学Java 「犬小哈」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噎。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

一句话结论

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

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

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

Tomcat 7 及以前的默认模型。

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

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

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.xml 的 Connector 里改 protocol 属性即可:

<!-- 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" />

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

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

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

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

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

文章目录
  1. 1. 一句话结论
  2. 2. BIO:一个连接一个线程,死等
  3. 3. NIO:多路复用,一个线程盯一堆连接
  4. 4. APR:绕过 JVM,直接跟内核对话
  5. 5. NIO2:听起来很美,用得很少
  6. 6. 一张表看懂四种模型的差异
  7. 7. 怎么配置:server.xml 里改一行的事
  8. 8. 三个常见误区,面试少说少失分