Java高并发Socket编程必须采用NIO+Selector多路复用:ServerSocketChannel须设为非阻塞并仅注册OP_ACCEPT;新连接需单独设非阻塞、注册同一Selector的OP_READ;读写需事件驱动、避免阻塞主线程。

Java 中 Socket 编程靠传统 ServerSocket.accept() 阻塞模型无法支撑万级并发,真正可行的路径是用 NIO + Selector 多路复用,单线程监听成千上万个连接,核心在于事件驱动、非阻塞、统一调度。
必须设为非阻塞并注册 OP_ACCEPT
这是整个机制的起点,缺一不可:
- 创建
ServerSocketChannel后,立即调用configureBlocking(false)—— 若遗漏,后续注册 Selector 会直接抛异常 - 绑定端口后,必须注册到 Selector 上,且只注册
SelectionKey.OP_ACCEPT - 不能注册
OP_READ或OP_WRITE,因为 ServerSocketChannel 只响应新连接事件,不负责收发数据
新连接到来时正确接纳并移交管理
在 select 循环中处理 OP_ACCEPT 事件:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 调用
serverChannel.accept()获取新的SocketChannel,该方法在非阻塞下可能返回null,必须判空 - 对这个新
SocketChannel同样执行configureBlocking(false)—— 父通道状态不继承,每条连接都得单独设置 - 将其注册到**同一个 Selector**,通常先注册
OP_READ(等数据真正到达再读),不要一接通就立刻 read() - 处理完当前
SelectionKey后,务必调用keyIterator.remove(),否则下次select()会重复触发
所有客户端连接由一个 Selector 统一调度读写
每个 SocketChannel 都注册在同一 Selector 上,共享事件循环:
立即学习“Java免费学习笔记(深入)”;
- 当某个 channel 的
isReadable()为 true,从其关联的ByteBuffer中读取数据;注意按协议解析(如长度前缀),避免粘包/半包 - 业务逻辑建议交由独立线程池处理,防止阻塞 Selector 所在的单线程
- 写操作一般先写入 buffer,再调用
channel.write(buffer);若未写完(buffer 仍有剩余),需注册OP_WRITE,等下次可写事件再继续
高并发下容易忽略的关键细节
代码能跑通 ≠ 能扛住万级连接:
- 每个新
SocketChannel的非阻塞设置不可省略,这是硬性要求 - accept() 后不要立刻 read(),TCP 连接刚建立时对方未必已发数据;应等待真正的
OP_READ事件再处理 - Selector 底层依赖操作系统机制(Linux 是 epoll),Java 自动适配,但需确保 JVM 参数和系统文件描述符限制(
ulimit -n)足够高 - 避免在 Selector 线程中做耗时操作(如 JSON 解析、DB 查询),否则整个事件循环被拖慢


















