Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

    • Java IO概述
    • IO基础

    • 常用IO流

    • 文件与目录操作

    • Java NIO

      • NIO概述
      • Buffer缓冲区
      • Channel通道
      • Selector多路复用
        • 1. 多路复用解决什么问题
        • 2. SelectionKey
        • 3. Selector 服务端骨架
        • 4. 为什么要 remove
        • 5. OP_WRITE 的特殊性
        • 6. Selector 和 Reactor 模式
        • 7. 常见问题
        • 专家实践清单
        • Tips 快问快答
        • 8. 总结
      • SocketChannel网络编程
      • NIO与IO对比
    • 高级主题

    • 实践与性能优化

    • 面试与设计题

    • IO实践速查

  • JVM

目录

Selector多路复用

Selector 是 Java NIO 网络编程的核心。它允许一个线程监听多个 Channel 的 IO 事件,从而避免传统 BIO 中“一个连接一个线程”的高成本模型。

# 1. 多路复用解决什么问题

传统阻塞 IO 中,如果一个线程调用 read() 后没有数据,它会阻塞在那里。为了同时处理多个连接,常见做法是每个连接分配一个线程。

连接数很大时会带来问题:

  • 线程数量多,占用内存。
  • 上下文切换频繁。
  • 大量线程处于阻塞状态,资源利用率低。

Selector 的思路是:

多个 Channel -> 注册到 Selector -> 一个线程等待就绪事件 -> 处理可读/可写/连接事件

# 2. SelectionKey

Channel 注册到 Selector 后会得到一个 SelectionKey。

常见事件:

  • SelectionKey.OP_ACCEPT:服务端接收连接。
  • SelectionKey.OP_CONNECT:客户端连接完成。
  • SelectionKey.OP_READ:通道可读。
  • SelectionKey.OP_WRITE:通道可写。

一个 Key 代表一个 Channel 在某个 Selector 上的注册关系。

# 3. Selector 服务端骨架

Selector selector = Selector.open();

ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
    selector.select();

    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        iterator.remove();

        if (key.isAcceptable()) {
            ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
            SocketChannel client = serverChannel.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
        } else if (key.isReadable()) {
            SocketChannel client = (SocketChannel) key.channel();
            ByteBuffer buffer = (ByteBuffer) key.attachment();

            int len = client.read(buffer);
            if (len == -1) {
                client.close();
                continue;
            }

            buffer.flip();
            while (buffer.hasRemaining()) {
                System.out.print((char) buffer.get());
            }
            buffer.clear();
        }
    }
}

这是最小骨架,真实生产代码还需要处理半包、粘包、写缓冲、连接关闭、异常清理、协议解析和线程模型。

# 4. 为什么要 remove

处理 selected key 后必须调用:

iterator.remove();

selectedKeys() 是已就绪事件集合。如果不移除,下一轮循环仍然会处理旧事件,导致重复处理。

# 5. OP_WRITE 的特殊性

OP_WRITE 表示通道当前可写。大多数 TCP 连接在大部分时间都是可写的,如果一直关注 OP_WRITE,Selector 可能不断返回写事件,造成 CPU 空转。

常见做法:

  • 只有待发送数据无法一次写完时,才注册 OP_WRITE。
  • 写完输出缓冲区后,取消对 OP_WRITE 的关注。

# 6. Selector 和 Reactor 模式

基于 Selector 的服务端通常符合 Reactor 模式:

  • Reactor:负责监听和分发事件。
  • Acceptor:处理新连接。
  • Handler:处理读写事件。
  • Worker:处理耗时业务逻辑。

Netty 就是基于类似思想构建的高性能网络框架,只是它封装了大量复杂细节。

# 7. 常见问题

  • 忘记设置 configureBlocking(false),注册 Selector 时会报错。
  • 忘记移除 selected key,导致重复处理。
  • 在 IO 线程中执行耗时业务,阻塞整个事件循环。
  • 写操作没有处理半包写出。
  • 协议解析没有处理 TCP 粘包和拆包。
  • 连接异常关闭时没有取消 key 和释放资源。

# 专家实践清单

关注点 建议 风险
事件处理 IO 线程只做轻量工作 业务阻塞拖慢所有连接
读事件 处理返回 -1 和异常 连接泄漏
写事件 只在有待写数据时注册 OP_WRITE 空转
Key 管理 关闭连接时取消 key 无效 key 堆积
协议解析 维护连接级状态 粘包拆包错误

Selector 事件循环:

select 等待事件
  ▼
遍历 selectedKeys
  ▼
处理 accept/read/write/connect
  ▼
更新兴趣事件
  ▼
回到 select

# Tips 快问快答

Q:Selector 解决什么问题? A:让一个线程监听多个 Channel 的就绪事件。

Q:Selector 是不是直接读写数据? A:不是,它只告诉你哪些 Channel 就绪,读写仍由 Channel 完成。

Q:OP_READ 表示什么? A:通道有数据可读或连接状态发生可读相关变化。

Q:为什么 OP_WRITE 不能一直注册? A:大多数时候 Socket 都可写,长期注册会造成事件循环空转。

Q:selectedKeys 处理完为什么要移除? A:避免下次循环重复处理同一个事件。

Q:Selector 适合阻塞业务逻辑吗? A:不适合,耗时业务应交给业务线程池。

Q:Selector 和 Reactor 有什么关系? A:Selector 是 Java NIO 多路复用基础,Reactor 是基于事件分发的架构模式。

Q:手写 Selector 最大难点是什么? A:连接状态、半包、半写、异常关闭和背压处理。

# 8. 总结

Selector 是 NIO 支撑高并发网络连接的关键。它把“等待多个连接事件”的工作交给一个选择器线程完成,从而显著减少线程数量。掌握 Selector 是理解 Netty、Redis 单线程事件模型、Reactor 模式的重要基础。

上次更新: 2026/06/25, 14:19:18
Channel通道
SocketChannel网络编程

← Channel通道 SocketChannel网络编程→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式