1. 五大 IO 模型?同步/异步与阻塞/非阻塞的维度?

  1. 阻塞 IO(BIO):read 一直等数据到达。一连接一线程
  2. 非阻塞 IO(NIO 轮询):read 立即返回"没数据",应用反复轮询(CPU 浪费)
  3. IO 多路复用:select/poll/epoll 一次等多个 fd,有事件才处理(NIO 的核心)
  4. 信号驱动 IO:数据就绪发 SIGIO 信号通知(用得少)
  5. 异步 IO(AIO):内核把数据拷贝到用户缓冲区完成后才通知(真正的异步)

两个维度:

🎯 面试要点

  • Java 的 NIO 是"多路复用"不是"异步"——命名易误解,要讲清楚
  • Linux 上 Java AIO 底层用 epoll 模拟(不是真异步),性能优势不明显,所以主流是 NIO(Netty)
  • Linux 真异步是 io_uring(新内核)——提一句加分

2. select、poll、epoll 的区别?

对比 select poll epoll
数据结构fd 位图(1024 上限)fd 数组(无上限)红黑树 + 就绪链表
遍历每次调用全量拷贝+遍历 O(n)同 select O(n)只返回就绪 fd(回调挂链表)O(就绪数)
用户态/内核态拷贝每次全量拷贝每次全量拷贝只拷贝就绪事件(epoll_wait)

epoll 三大操作:epoll_create(建表)、epoll_ctl(增删改监听 fd,回调机制注册)、epoll_wait(等就绪)。水平触发 LT vs 边缘触发 ET:LT 有数据就反复通知,ET 只在状态变化通知一次(配非阻塞读循环)。

🎯 面试要点

  • epoll 适合"连接多、活跃少"(高并发服务器);select 适合连接少
  • Netty 可用 epoll 原生传输(epoll 模式)。注意:JDK 的 Selector 在 Linux 上底层也是 epoll,不是 select;Netty native epoll 的优势在于边缘触发、多线程 accept 与更少的数据结构开销
  • ET 模式性能更高但要小心漏读(while 读到 EAGAIN)

3. Reactor 模型?Netty 如何实现?

Reactor(反应器):事件驱动——一个线程等所有连接的事件(多路复用),事件到达分发给 Handler。Netty 的基石。三种形态:

Netty 核心组件
EventLoopGroup bossGroup = new NioEventLoopGroup(1);     // accept 线程
EventLoopGroup workerGroup = new NioEventLoopGroup();        // IO 线程(默认核数×2)

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
 .channel(NioServerSocketChannel.class)
 .childHandler(new ChannelInitializer<SocketChannel>() {
     @Override protected void initChannel(SocketChannel ch) {
         ch.pipeline()
           .addLast(new LengthFieldBasedFrameDecoder(...))   // 粘包/拆包解决
           .addLast(new StringDecoder())                     // 解码器(责任链)
           .addLast(new MyHandler());                        // 业务处理器
     }
 });

🎯 面试要点

  • Netty 四大核心:EventLoop(线程模型)、Channel/Pipeline(责任链处理)、ByteBuf(零拷贝)、Future/Promise(异步回调)
  • 零拷贝:Netty 的 FileRegion(sendfile)、DirectBuffer 直接内存(见 JVM 模块)
  • 粘包/拆包解决:长度前缀(LengthFieldBasedFrameDecoder)、分隔符、固定长度——Netty 面试必问

🎤 常见面试追问

  1. 阻塞/非阻塞 和 同步/异步 是两个维度吗?——是:阻塞/非阻塞是"调用是否立即返回"(应用视角);同步/异步是"数据拷贝谁完成"(内核完成后才通知 = 异步)。BIO/NIO 是同步(1~4 模型),AIO 才异步。
  2. select、poll、epoll 的核心区别?——select/poll 每次调用全量扫描 fd(O(n) 且 select 有 1024 上限);epoll 用红黑树登记 + 就绪链表回调,只返回就绪的(O(就绪数))。高并发服务器选 epoll。
  3. Netty 为什么快?——主从 Reactor 线程模型 + IO 多路复用(epoll)+ 零拷贝(DirectBuffer/sendfile)+ 池化 ByteBuf + 无锁串行设计。
  4. 粘包/拆包怎么解决?——TCP 是字节流无边界:固定长度 / 分隔符 / 长度前缀(最常用)。Netty 的 LengthFieldBasedFrameDecoder 就是长度前缀实现。
  5. Tomcat 用什么 IO 模型?——Tomcat 8.5+ 默认 NIO(多路复用),支持多个连接共享线程;BIO 是旧版(一连接一线程)已废弃。

📖 名词解释(本页术语)

术语 大白话解释
IO 多路复用一个线程同时"等"成百上千个连接的事件(select/poll/epoll),有事件才处理——高并发的核心。
select / poll / epollLinux 三代多路复用 API:epoll 性能最好(事件驱动、只返回就绪 fd),Netty/nginx 都用它。
Reactor 模型事件驱动架构:一个线程等所有连接的事件,事件来了分发给 Handler。Netty 的主从 Reactor 是最佳实践。
NettyJava 最流行的高性能网络框架(NIO 封装):Dubbo/gRPC/大数据框架底层都用它。
粘包 / 拆包TCP 字节流没有消息边界:多条消息黏在一起(粘包)或一条被拆开(拆包)。应用层要定义边界(长度头)。
零拷贝数据从磁盘到网卡不走用户态(sendfile/DMA),省多次拷贝和上下文切换——Kafka/Netty 性能关键。
Channel / Buffer / SelectorJava NIO 三件套:通道(连接)、缓冲区(数据)、选择器(多路复用监听)。
EAGAIN非阻塞 IO 的"没数据"返回码:读循环要处理它(读到 EAGAIN 说明读完了),否则可能死循环或漏读。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。