面试知识库
进阶

BIO-NIO-AIO对比#

一句话答案#

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

核心要点

三种 IO 模型对比:

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

BIO 的问题:

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

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

// 一个线程管理多个 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()) { /* 处理读事件 */ }
    }
}
java

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 数量多少,只返回就绪的)
plaintext

AIO(NIO 2):

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

Java 中有三种 IO 模型。BIO 是同步阻塞模型,每个连接需要一个独立线程处理,线程在读写时阻塞等待,并发量大时线程数爆炸,上下文切换和内存开销巨大,只适合并发量小的简单场景。NIO 是同步非阻塞模型,核心机制是 IO 多路复用,通过 Selector 一个线程就能监听多个 Channel 的事件,哪个连接有数据就绪就处理哪个。NIO 有三大核心组件:Channel 是双向的数据通道,Buffer 是读写数据的缓冲区,Selector 是事件多路复用器。底层在 Linux 上基于 epoll 实现,epoll 使用红黑树管理文件描述符和就绪队列,只返回有事件就绪的连接,时间复杂度 O(1),比 select 和 poll 的 O(n) 遍历高效得多。AIO 是异步非阻塞模型,IO 操作完全由内核完成后回调通知应用,但 Linux 的异步 IO 支持不完善,Java AIO 在 Linux 上底层仍用 epoll 模拟,实际收益有限。生产环境主流方案是 NIO 加 Netty 框架,Netty 封装了原生 NIO 的复杂性并解决了 epoll bug 和半包粘包问题。

追问与易错

追问方向:

  • “NIO 的三大核心组件是什么?”→ Channel(双向数据通道)、Buffer(读写缓冲区)、Selector(事件多路复用器),三者配合实现一个线程管理多连接
  • “为什么生产环境用 Netty 而不是原生 NIO?”→ 原生 NIO 的 API 复杂易出错,存在 epoll 空轮询 bug,且需要自行处理半包粘包、断线重连等问题,Netty 封装了这些细节
  • “AIO 在 Linux 上为什么推广不开?”→ Linux 的异步 IO 实现不完善,Java AIO 在 Linux 上底层仍用 epoll 模拟,性能没有实质提升;而 NIO + epoll 已经足够满足高并发需求

易错点:

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