跳到主要内容

IO 模型

问题

Linux 的 5 种 IO 模型是什么?同步/异步、阻塞/非阻塞怎么区分?

答案

5 种 IO 模型

IO 操作分两个阶段:① 等待数据就绪 ② 将数据从内核拷贝到用户空间。

模型等待数据数据拷贝说明
同步阻塞(BIO)阻塞阻塞两阶段都阻塞
同步非阻塞非阻塞(轮询)阻塞不断询问"好了没?"
IO 多路复用阻塞在 select/epoll阻塞一个线程监听多个 fd
信号驱动 IO非阻塞(信号通知)阻塞内核数据就绪后发信号
异步 IO(AIO)非阻塞非阻塞全程非阻塞,内核完成后回调

形象比喻

  • BIO:你在餐厅排队等位,一直站着等(什么都干不了)
  • NIO 轮询:你不断跑去问"有位子了吗?"
  • IO 多路复用:你留了电话号叫号器响了通知你(一个叫号器管多桌)
  • AIO:你点了外卖,送到家门口打电话叫你(过程全自动)

IO 多路复用

一个线程通过 select/poll/epoll 监听多个文件描述符(Socket),哪个就绪就处理哪个。这就是 Reactor 模式的底层原理。

同步/异步 vs 阻塞/非阻塞

概念区分维度说明
同步/异步谁来完成 IO同步:用户线程自己读;异步:内核完成后通知用户
阻塞/非阻塞等待时线程是否挂起阻塞:线程挂起等待;非阻塞:立即返回
关键区分
  • IO 多路复用同步的(数据拷贝仍然由用户线程执行),但比 BIO 高效(一个线程管理多个连接)
  • 真正的异步 IO(AIO)在 Linux 上还不够成熟,实际项目中用 IO 多路复用(epoll)+ Reactor 更多

常见面试问题

Q1: Java NIO 对应哪种 IO 模型?

答案

Java NIO 使用 Selector,底层是 IO 多路复用(Linux 使用 epoll)。注意 Java 的 NIO 不是"非阻塞 IO"的意思,而是 New IO。

Selector 注册多个 Channel,调用 select() 阻塞等待就绪事件,有事件后逐个处理。详见 Socket 编程

Q2: 为什么 Netty 不用 AIO?

答案

  1. Linux 的 AIO 对网络 IO 支持不好,底层还是用 epoll 模拟
  2. epoll 已经足够高效(事件通知 O(1))
  3. Netty 基于 epoll 的 Reactor 模型已经非常成熟

AIO 在 Windows 上(IOCP)较完善,但 Java 服务端主要部署在 Linux。

Q3: Reactor 模式是什么?

答案

Reactor 模式是基于 IO 多路复用的事件驱动模型:

模型说明
单 Reactor 单线程一个线程负责监听和处理(Redis 6.0 前)
单 Reactor 多线程一个线程监听,多线程处理
主从 Reactor主 Reactor 接收连接,从 Reactor 处理 IO(Netty)

相关链接