Skip to content
进阶

一句话答案

BIO 同步阻塞一连接一线程,NIO 同步非阻塞基于 Selector 多路复用,AIO 异步非阻塞由 OS 回调通知。

核心要点

三种 IO 模型对比:

模型全称特点适用场景
BIOBlocking IO(同步阻塞)一个连接一个线程,读/写时阻塞并发量小、逻辑简单
NIONon-blocking IO(同步非阻塞)多路复用,一个线程处理多连接高并发,如 Netty
AIOAsynchronous IO(异步非阻塞)内核通知应用数据已就绪Linux 支持有限,较少用

BIO 的问题:

java
// 每个连接占用一个线程
ServerSocket server = new ServerSocket(8080);
while (true) {
    Socket socket = server.accept();   // 阻塞,等待连接
    new Thread(() -> handle(socket)).start();  // 每连接开一个线程
}
// 10万并发 = 10万线程,内存/CPU 上下文切换开销巨大

NIO 的核心:Selector(多路复用)

java
// 一个线程管理多个 Channel
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);       // 设置非阻塞
server.register(selector, SelectionKey.OP_ACCEPT);  // 注册感兴趣的事件

while (true) {
    selector.select();  // 阻塞,直到有事件就绪
    Set<SelectionKey> keys = selector.selectedKeys();
    for (SelectionKey key : keys) {
        if (key.isAcceptable()) { /* 处理连接 */ }
        if (key.isReadable()) { /* 处理读事件 */ }
    }
}

NIO 的三大核心:

  • Channel(通道):双向,支持非阻塞读写
  • Buffer(缓冲区):读写数据的中间层,必须通过 Buffer 操作
  • Selector(选择器):监听多个 Channel 的事件,事件就绪时通知应用

NIO 底层原理(Linux epoll):

select/poll(轮询):
  每次调用将所有 fd 传入内核,内核遍历所有 fd 检查就绪状态
  O(n) 遍历,fd 数量多时性能差

epoll(事件驱动):
  epoll_create:内核创建 epoll 实例(红黑树 + 就绪队列)
  epoll_ctl:将 fd 注册到内核红黑树中(只需注册一次)
  epoll_wait:阻塞等待,内核将就绪 fd 放入就绪队列,直接返回就绪 fd
  O(1) 等待(无论总 fd 数量多少,只返回就绪的)

AIO(NIO 2):

  • IO 操作完全由内核异步完成,完成后回调通知应用
  • Java 的 AsynchronousSocketChannel 实现
  • Linux 的异步 IO(io_uring)支持有限,实际上 Java AIO 在 Linux 上底层仍用 epoll 模拟
  • 实际项目中 Netty 等框架基于 NIO 即可满足需求
追问与易错

追问方向:

  • NIO 的三大核心组件是什么?(Channel/Buffer/Selector)
  • 为什么生产环境用 Netty 而不是原生 NIO?(API 复杂/epoll bug/半包粘包处理)
  • AIO 在 Linux 上为什么推广不开?(Linux AIO 实现不完善,epoll 够用)

易错点:

  • ❌ "NIO 就是非阻塞IO"——NIO 是 New IO,包含非阻塞+多路复用+Buffer
  • ❌ 混淆 IO 多路复用和异步 IO——多路复用仍是同步的(就绪通知后自己读)

💡 记忆锚点

BIO = 餐厅一桌配一个服务员干等;NIO = 一个服务员盯着叫号屏(Selector/epoll),哪桌亮灯就去服务;AIO = 后厨做好直接端上桌,服务员连盯屏幕都省了。生产环境NIO+Netty是主流。