多线程 Reactor 模式

目录

[多线程 Reactor 模式的核心动机](#多线程 Reactor 模式的核心动机)

多线程演进方向

[多线程 Reactor 模型结构](#多线程 Reactor 模型结构)

[多线程 EchoServer 实现核心部分](#多线程 EchoServer 实现核心部分)

[Handler 的多线程化](#Handler 的多线程化)

[多线程 Reactor 的三个核心点](#多线程 Reactor 的三个核心点)


本篇文章内容的前置知识为 单线程 Reactor 模式,如果不了解,可点击链接学习
单线程 Reactor 模式-CSDN博客

多线程 Reactor 模式的核心动机

原因:单线程 Reactor 中,Reactor 线程和所有 Handler 处理器都共用同一个线程,一旦某个 Handler 执行时间较长,所有事件的处理都会被阻塞,系统无法横向扩展。

目标:将 Reactor 和 Handler 的职责分离到多个线程中,提高并发能力,适应高连接数和重业务处理场景。

多线程演进方向

(1)升级 Handler:

将负责 IO 读写和业务逻辑的 Handler 放入独立的线程池中异步处理,提高吞吐。

(2)升级 Reactor:

使用多个 Selector,创建多个子反应器(SubReactor),每个负责独立的选择操作。

多线程 Reactor 模型结构

客户端请求

Reactor1(主反应器) → accept handler(接收连接)

↓ 分发给

Reactor2(子反应器) → read/decode/compute/encode/write handler

Reactor 1 专职监听新连接事件;

Reactor 2 专职监听 IO 读写事件;

多个 Handler 在不同线程池中处理,互不阻塞。

多线程 EchoServer 实现核心部分

Reactor 主类

java 复制代码
Selector[] selectors = new Selector[2];

一个 selector 专管连接接收(OP_ACCEPT);

一个 selector 专管数据读写(OP_READ/WRITE)。

java 复制代码
SubReactor subReactor1 = new SubReactor(selectors[0]);
SubReactor subReactor2 = new SubReactor(selectors[1]);

为每个 selector 启动一个线程,轮询事件。

SubReactor 逻辑

子反应器的职责是:

负责轮询 selector 中是否有事件发生;

若有事件触发,就调用 dispatch() 方法,执行 handler。

java 复制代码
SelectionKey sk = it.next();
Runnable handler = (Runnable) sk.attachment();
handler.run();

从 selectionKey 中拿出 attach 的 Handler;

直接执行 handler 的 run() 方法。

AcceptorHandler(连接处理器)

java 复制代码
SocketChannel channel = serverSocket.accept();
new MultiThreadEchoHandler(selectors[1], channel);

接收连接后创建 IO Handler;

并将其注册到数据读写 selector(selectors[1])中。

Handler 的多线程化

目标:将读写处理逻辑从 IO 事件轮询线程中分离,提交到线程池异步处理。

核心实现:

java 复制代码
static ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> asyncRun());

static ExecutorService pool = Executors.newFixedThreadPool(4);

创建一个包含 4 个线程的固定大小线程池,专门用于执行具体的业务逻辑(如数据处理、计算等)。线程池会复用这 4 个线程,避免频繁创建销毁线程的开销。

pool.submit(() -> asyncRun());

将实际的业务处理逻辑(asyncRun() 方法,比如读取数据、回显数据的逻辑)提交到线程池执行。这样做的核心目的是 让 Reactor 线程(负责监听和分发事件)不用等待业务处理完成,可以立即回去处理其他 IO 事件,避免被耗时操作阻塞。

asyncRun() 方法 的代码思路和单线程 Reactor 模式核心 run() 方法 代码思路类似

单线程 Reactor 模式因 单线程瓶颈 ,仅适用于简单场景;

Connection Per Thread 因 线程爆炸 问题,已被淘汰;

多线程 Reactor 模式通过 事件分离 + 线程池 解决了前两者的缺陷

多线程 Reactor 的三个核心点

模块 处理职责 所在线程
Main Reactor 监听连接(OP_ACCEPT) 主线程
Sub Reactor 监听读写事件(OP_READ / OP_WRITE) 线程1、线程2(子线程)
Handler(多线程) 实际执行 IO + 业务逻辑 提交给线程池异步执行

优势对比

模型 是否并发 是否线程隔离 是否支持线程池
单线程 Reactor
多线程 Reactor

Reactor 模式的优缺点

一、Reactor 模式与其他模式对比

(1)Reactor 模式 vs 生产者-消费者模式

相似之处:

二者都具有事件驱动特性。

在生产者-消费者模型中:

生产者产生任务(事件)并放入一个队列。

消费者从队列中拉取(pull)任务并处理。

Reactor 模式下:

客户端连接、读写事件相当于生产者。

Selector 检查事件是否就绪,相当于从"系统缓冲队列"中拉取事件。

Handler 负责处理事件,相当于消费者。

不同之处:

生产者-消费者模型是基于「消息队列」的显式交互;

Reactor 模式是基于 IO 多路复用的「查询式机制」(如 select、epoll)

Reactor 没有明确的队列,而是通过系统底层缓存区查询到事件,再立即由 Dispatcher 分发给对应 Handler 处理。

(2)Reactor 模式 vs 观察者模式

相似之处:

两者都体现了事件监听 + 响应处理的思想。

Reactor 中,Selector 检测到事件 → Dispatcher 分发给对应 Handler。

观察者模式中,主题 Topic 发生变化 → 通知所有订阅该主题的观察者 Observer。

都涉及"注册监听 + 回调响应"的机制。

不同之处:

观察者模式允许一个事件被多个观察者处理(一对多)

而 Reactor 模式中,一个 IO 事件只能交给一个 Handler 实例处理(一对一)

即:事件和处理器的绑定是唯一的,Handler 和 SelectionKey 是一一对应的。

二、Reactor 模式的优点

高响应性:

单线程模式:通过 非阻塞 IO + 事件驱动,避免了传统阻塞 IO 中 一个连接阻塞导致全系统卡住 的问题(仅当 Handler 无阻塞操作时有效)

多线程模式:进一步通过 Reactor 线程与业务线程分离,即使某个业务逻辑阻塞,也不会影响 Reactor 线程处理其他连接,响应性更优。

编程简单:

单线程模式:天然无多线程共享数据问题,不需要锁和同步,逻辑最简洁。

多线程模式:通过 职责分离(Reactor 线程管 IO,业务线程管逻辑),减少了共享数据的场景,相比 一连接一线程 模式,锁和同步的复杂度更低。

良好的可扩展性:

多线程模式:可通过增加 Reactor 线程数(利用多核 CPU)、扩大业务线程池(处理更多并发业务)显著提升吞吐量,扩展性更强。

三、Reactor 模式的缺点

模型实现较复杂:

相较于简单的阻塞 IO,Reactor 涉及 Selector 注册、事件派发、状态管理,门槛较高;

不易调试,错误不易定位。

强依赖系统支持:

依赖于操作系统提供的高效 IO 多路复用机制(如 Linux 的 epoll);

若底层不支持 IO 复用,性能优势无法体现,甚至可能适得其反。

Handler 内部阻塞会影响整体反应能力:

如果某个 Handler 内部业务逻辑阻塞太久(比如读取大文件、等待数据库响应),会导致整个 Selector 线程卡住,从而影响其他 IO 事件的派发,形成伪非阻塞。

模式 特点 是否规避伪非阻塞?
多 Reactor + 同步 Handler 多线程负责 Selector,Handler 仍由 Selector 线程执行 不能规避
多 Reactor + 异步 Handler(线程池) Selector 线程只分发事件,Handler 放入线程池处理 能完全规避

同步异步handler主要是看它的run方法里面有没有包含耗时/阻塞操作,比如read() / write()

伪非阻塞 指的是表面上使用了非阻塞 IO(如 NIO 的 SocketChannel 配置为非阻塞模式)和 Reactor 事件驱动模型,但由于某个环节(通常是 Handler 处理逻辑)出现阻塞,导致整个 Reactor 线程被卡住,最终失去了非阻塞模式应有的并发处理能力。

相关推荐
oioihoii6 分钟前
C++实战案例:从static成员到线程安全的单例模式
java·c++·单例模式
a cool fish(无名)34 分钟前
rust-参考与借用
java·前端·rust
Spliceㅤ44 分钟前
Spring框架
java·服务器·后端·spring·servlet·java-ee·tomcat
xzkyd outpaper1 小时前
ConcurrentHashMap 如何保证线程安全(2)
java·计算机八股
灵典3361 小时前
JavaSE-图书信息管理系统
java·开发语言
淮北枳丶1 小时前
Java常用命令、JVM常用命令
java·开发语言·jvm
天天进步20152 小时前
设计模式在Java中的实际应用:单例、工厂与观察者模式详解
java·观察者模式·设计模式
thginWalker2 小时前
Java JVM
java·jvm
是Yu欸2 小时前
【浏览器插件冲突】Edge浏览器加载不出来CSDN创作者中心
java·数据库·edge
中东大鹅2 小时前
SpringBoot配置文件
java·spring boot·spring