目前存在的线程模型有:
- 传统阻塞 IO 服务模型
- Reactor 模式, 根据 Reactor 数量和处理资源池线程的数量不同, 又有 3 重典型的实现.
- 单 Reactor 单线程
- 单 Reactor 多线程
- 主从 Reactor 多线程.
传统阻塞 IO 服务模型
- 一个线程对应一个客户端, 如果客户端过多就会占用很大的系统资源.
- 没有可读数据时, 线程会阻塞, 造成资源浪费.

Reactor 模式
下图只是对 Reactor 模式的一个整体理念, 当涉及到三个具体实现时, 有不同的变化.

基于 I/O 复用模型: 多个连接共用一个阻塞对象, 应用程序只需要在一个阻塞对象等待, 无需阻塞等待所有连接. 当某个连接有新的数据可以处理时, 操作系统通知应用程序, 线程从阻塞状态返回, 开始业务处理.
基于线程池复用线程资源: 不必再为每个连接创建线程, 将连接完成后的业务处理任务分配给线程进行处理, 一个线程可以处理多个连接的业务.
就是说, 服务处理器使用 I/O 复用监听一个或多个客户端的输入事件, 然后将这些事件分发到不同的线程处理.
也可以简单粗暴的理解为: IO多路复用 + 线程池 = Reactor 模式
单 Reactor 单线程

- Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发;
- 如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后的后续业务处理;
- 如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应;
- Handler 会完成 Read→业务处理→Send 的完整业务流程。
优点: 模型简单, 没有多线程、进程通信、竞争的问题, 全部都在一个线程中完成.
缺点: 性能问题, 只有一个线程, 无法完全发挥多核 CPU 的性能. Handler 在处理某个连接上的业务时, 整个进程无法处理其他连接事件, 很容易导致性能瓶颈.
可靠性问题, 线程意外跑飞, 或者进入死循环, 会导致整个系统通信模块不可用, 不能接收和处理外部消息, 造成节点故障.
使用场景: 客户端的数量有限, 业务处理非常快速, 比如 Redis, 业务处理的时间复杂度 O(1).
单 Reactor 多线程

-
Reactor 对象通过 Select 监控客户端请求事件, 收到事件后通过 Dispatch 进行分发.
-
如果是建立连接请求事件, 则由 Acceptor 通过 Accept 处理连接请求, 然后创建一个 Handler 对象处理连接完成后续的各种事件.
-
如果不是建立连接事件, 则 Reactor 会分发调用连接对应的 Handler 来响应.
-
Handler 只负责响应事件, 不做具体业务处理, 通过 Read 读取数据后, 会分发给后面的 Worker 线程池进行业务处理.
-
Worker 线程池会分配独立的线程完成真正的业务处理, 如何将响应结果发给 Handler 进行处理.
-
Handler 收到响应结果后通过 Send 将响应结果返回给 Client.
优点: 可以充分利用多核 CPU 的处理能力.
缺点: 多线程数据共享和访问比较复杂; Reactor 承担所有事件的监听和响应, 在单线程中运行, 高并发场景下容易成为性能瓶颈.
主从 Reactor 多线程

这种模式是对单 Reactor 的改进, 由原来单 Reactor 改成了 Reactor 主线程与 Reactor 子线程.
- Reactor 主线程的 MainReactor 对象通过
select监听连接事件, 收到事件后, 通过 Acceptor 处理连接事件. - 当 Acceptor 处理完连接事件之后, MainReactor 将连接分配给 SubReactor.
- SubReactor 将连接加入到连接队列进行监听, 并创建
handler进行事件处理. - 当有新的事件发生时, SubReactor 就会调用对应的
handler处理. - handler 通过
read读取数据, 交由 Worker 线程池处理业务. - Worker 线程池分配线程处理完数据后, 将结果返回给
handler. handler收到返回的数据后, 通过send将结果返回给客户端.
MainReactor 可以对应多个 SubReactor.
这种优点多多,各个模块各司其职,缺点就是实现复杂。