上文说到单线程的reactor模式 reactor模式:单线程的reactor模式

单线程的reactor模式并没有解决IO和CPU处理速度不匹配问题,所以多线程的reactor模式引入线程池的概念,把耗时的IO操作交由线程池处理,处理完了之后再同步到selectionkey中,服务器架构图如下

上文(reactor模式:单线程的reactor模式)提到,以read和send阶段IO最为频繁,所以多线程的reactor版本里,把这2个阶段单独拎出来。

下面看看代码实现

 1 // Reactor線程 (该类与单线程的处理基本无变动)
2 package server;
3
4 import java.io.IOException;
5 import java.net.InetSocketAddress;
6 import java.nio.channels.SelectionKey;
7 import java.nio.channels.Selector;
8 import java.nio.channels.ServerSocketChannel;
9 import java.util.Iterator;
10 import java.util.Set;
11
12 public class TCPReactor implements Runnable {
13
14 private final ServerSocketChannel ssc;
15 private final Selector selector;
16
17 public TCPReactor(int port) throws IOException {
18 selector = Selector.open();
19 ssc = ServerSocketChannel.open();
20 InetSocketAddress addr = new InetSocketAddress(port);
21 ssc.socket().bind(addr); // 在ServerSocketChannel綁定監聽端口
22 ssc.configureBlocking(false); // 設置ServerSocketChannel為非阻塞
23 SelectionKey sk = ssc.register(selector, SelectionKey.OP_ACCEPT); // ServerSocketChannel向selector註冊一個OP_ACCEPT事件,然後返回該通道的key
24 sk.attach(new Acceptor(selector, ssc)); // 給定key一個附加的Acceptor對象
25 }
26
27 @Override
28 public void run() {
29 while (!Thread.interrupted()) { // 在線程被中斷前持續運行
30 System.out.println("Waiting for new event on port: " + ssc.socket().getLocalPort() + "...");
31 try {
32 if (selector.select() == 0) // 若沒有事件就緒則不往下執行
33 continue;
34 } catch (IOException e) {
35 // TODO Auto-generated catch block
36 e.printStackTrace();
37 }
38 Set<SelectionKey> selectedKeys = selector.selectedKeys(); // 取得所有已就緒事件的key集合
39 Iterator<SelectionKey> it = selectedKeys.iterator();
40 while (it.hasNext()) {
41 dispatch((SelectionKey) (it.next())); // 根據事件的key進行調度
42 it.remove();
43 }
44 }
45 }
46
47 /*
48 * name: dispatch(SelectionKey key)
49 * description: 調度方法,根據事件綁定的對象開新線程
50 */
51 private void dispatch(SelectionKey key) {
52 Runnable r = (Runnable) (key.attachment()); // 根據事件之key綁定的對象開新線程
53 if (r != null)
54 r.run();
55 }
56
57 }
 1  // 接受連線請求線程
2 package server;
3
4 import java.io.IOException;
5 import java.nio.channels.SelectionKey;
6 import java.nio.channels.Selector;
7 import java.nio.channels.ServerSocketChannel;
8 import java.nio.channels.SocketChannel;
9
10 public class Acceptor implements Runnable {
11
12 private final ServerSocketChannel ssc;
13 private final Selector selector;
14
15 public Acceptor(Selector selector, ServerSocketChannel ssc) {
16 this.ssc=ssc;
17 this.selector=selector;
18 }
19
20 @Override
21 public void run() {
22 try {
23 SocketChannel sc= ssc.accept(); // 接受client連線請求
24 System.out.println(sc.socket().getRemoteSocketAddress().toString() + " is connected.");
25
26 if(sc!=null) {
27 sc.configureBlocking(false); // 設置為非阻塞
28 SelectionKey sk = sc.register(selector, SelectionKey.OP_READ); // SocketChannel向selector註冊一個OP_READ事件,然後返回該通道的key
29 selector.wakeup(); // 使一個阻塞住的selector操作立即返回
30 sk.attach(new TCPHandler(sk, sc)); // 給定key一個附加的TCPHandler對象
31 }
32
33 } catch (IOException e) {
34 // TODO Auto-generated catch block
35 e.printStackTrace();
36 }
37 }
38
39
40 }
 1     // Handler線程
2 package server;
3
4 import java.io.IOException;
5 import java.nio.channels.SelectionKey;
6 import java.nio.channels.SocketChannel;
7 import java.util.concurrent.LinkedBlockingQueue;
8 import java.util.concurrent.ThreadPoolExecutor;
9 import java.util.concurrent.TimeUnit;
10
11 public class TCPHandler implements Runnable {
12
13 private final SelectionKey sk;
14 private final SocketChannel sc;
15 private static final int THREAD_COUNTING = 10;
16 private static ThreadPoolExecutor pool = new ThreadPoolExecutor(
17 THREAD_COUNTING, THREAD_COUNTING, 10, TimeUnit.SECONDS,
18 new LinkedBlockingQueue<Runnable>()); // 線程池
19
20 HandlerState state; // 以狀態模式實現Handler
21
22 public TCPHandler(SelectionKey sk, SocketChannel sc) {
23 this.sk = sk;
24 this.sc = sc;
25 state = new ReadState(); // 初始狀態設定為READING
26 pool.setMaximumPoolSize(32); // 設置線程池最大線程數
27 }
28
29 @Override
30 public void run() {
31 try {
32 state.handle(this, sk, sc, pool);
33
34 } catch (IOException e) {
35 System.out.println("[Warning!] A client has been closed.");
36 closeChannel();
37 }
38 }
39
40 public void closeChannel() {
41 try {
42 sk.cancel();
43 sc.close();
44 } catch (IOException e1) {
45 e1.printStackTrace();
46 }
47 }
48
49 public void setState(HandlerState state) {
50 this.state = state;
51 }
52 }
53
54
 1     package server;
2
3 import java.io.IOException;
4 import java.nio.channels.SelectionKey;
5 import java.nio.channels.SocketChannel;
6 import java.util.concurrent.ThreadPoolExecutor;
7
8 public interface HandlerState {
9
10 public void changeState(TCPHandler h);
11
12 public void handle(TCPHandler h, SelectionKey sk, SocketChannel sc,
13 ThreadPoolExecutor pool) throws IOException ;
14 }
 1     package server;
2
3 import java.io.IOException;
4 import java.nio.ByteBuffer;
5 import java.nio.channels.SelectionKey;
6 import java.nio.channels.SocketChannel;
7 import java.util.concurrent.ThreadPoolExecutor;
8
9 public class ReadState implements HandlerState{
10
11 private SelectionKey sk;
12
13 public ReadState() {
14 }
15
16 @Override
17 public void changeState(TCPHandler h) {
18 // TODO Auto-generated method stub
19 h.setState(new WorkState());
20 }
21
22 @Override
23 public void handle(TCPHandler h, SelectionKey sk, SocketChannel sc,
24 ThreadPoolExecutor pool) throws IOException { // read()
25 this.sk = sk;
26 // non-blocking下不可用Readers,因為Readers不支援non-blocking
27 byte[] arr = new byte[1024];
28 ByteBuffer buf = ByteBuffer.wrap(arr);
29
30 int numBytes = sc.read(buf); // 讀取字符串
31 if(numBytes == -1)
32 {
33 System.out.println("[Warning!] A client has been closed.");
34 h.closeChannel();
35 return;
36 }
37 String str = new String(arr); // 將讀取到的byte內容轉為字符串型態
38 if ((str != null) && !str.equals(" ")) {
39 h.setState(new WorkState()); // 改變狀態(READING->WORKING)
40 pool.execute(new WorkerThread(h, str)); // do process in worker thread
41 System.out.println(sc.socket().getRemoteSocketAddress().toString()
42 + " > " + str);
43 }
44
45 }
46
47 /*
48 * 執行邏輯處理之函數
49 */
50 synchronized void process(TCPHandler h, String str) {
51 // do process(decode, logically process, encode)..
52 // ..
53 h.setState(new WriteState()); // 改變狀態(WORKING->SENDING)
54 this.sk.interestOps(SelectionKey.OP_WRITE); // 通過key改變通道註冊的事件
55 this.sk.selector().wakeup(); // 使一個阻塞住的selector操作立即返回
56 }
57
58 /*
59 * 工作者線程
60 */
61 class WorkerThread implements Runnable {
62
63 TCPHandler h;
64 String str;
65
66 public WorkerThread(TCPHandler h, String str) {
67 this.h = h;
68 this.str=str;
69 }
70
71 @Override
72 public void run() {
73 process(h, str);
74 }
75
76 }
77 }
 1  package server;
2
3 import java.io.IOException;
4 import java.nio.channels.SelectionKey;
5 import java.nio.channels.SocketChannel;
6 import java.util.concurrent.ThreadPoolExecutor;
7
8 public class WorkState implements HandlerState {
9
10 public WorkState() {
11 }
12
13 @Override
14 public void changeState(TCPHandler h) {
15 // TODO Auto-generated method stub
16 h.setState(new WriteState());
17 }
18
19 @Override
20 public void handle(TCPHandler h, SelectionKey sk, SocketChannel sc,
21 ThreadPoolExecutor pool) throws IOException {
22 // TODO Auto-generated method stub
23
24 }
25
26 }
 1     package server;
2
3 import java.io.IOException;
4 import java.nio.ByteBuffer;
5 import java.nio.channels.SelectionKey;
6 import java.nio.channels.SocketChannel;
7 import java.util.concurrent.ThreadPoolExecutor;
8
9 public class WriteState implements HandlerState{
10
11 public WriteState() {
12 }
13
14 @Override
15 public void changeState(TCPHandler h) {
16 // TODO Auto-generated method stub
17 h.setState(new ReadState());
18 }
19
20 @Override
21 public void handle(TCPHandler h, SelectionKey sk, SocketChannel sc,
22 ThreadPoolExecutor pool) throws IOException { // send()
23 // get message from message queue
24
25 String str = "Your message has sent to "
26 + sc.socket().getLocalSocketAddress().toString() + "\r\n";
27 ByteBuffer buf = ByteBuffer.wrap(str.getBytes()); // wrap自動把buf的position設為0,所以不需要再flip()
28
29 while (buf.hasRemaining()) {
30 sc.write(buf); // 回傳給client回應字符串,發送buf的position位置 到limit位置為止之間的內容
31 }
32
33 h.setState(new ReadState()); // 改變狀態(SENDING->READING)
34 sk.interestOps(SelectionKey.OP_READ); // 通過key改變通道註冊的事件
35 sk.selector().wakeup(); // 使一個阻塞住的selector操作立即返回
36 }
37 }
 1     package server;
2
3 import java.io.IOException;
4
5 public class Main {
6
7
8 public static void main(String[] args) {
9 // TODO Auto-generated method stub
10 try {
11 TCPReactor reactor = new TCPReactor(1333);
12 reactor.run();
13 } catch (IOException e) {
14 // TODO Auto-generated catch block
15 e.printStackTrace();
16 }
17 }
18
19 }

总的来说,多线程版本的reactor是为了解决单线程reactor版本的IO和CPU处理速度不匹配问题,从而达到高效处理的目的

参考文章:

https://blog.csdn.net/yehjordan/article/details/51017025

reactor模式:多线程的reactor模式的更多相关文章

  1. 两种高效的事件处理模式(Proactor和Reactor)

    典型的多线程服务器的线程模型 1. 每个请求创建一个线程,使用阻塞式 I/O 操作 这是最简单的线程模型,1个线程处理1个连接的全部生命周期.该模型的优点在于:这个模型足够简单,它可以实现复杂的业务场 ...

  2. 【转】Reactor与Proactor两种模式区别

    转自:http://www.cnblogs.com/cbscan/articles/2107494.html 两种IO多路复用方案:Reactor and Proactor 一般情况下,I/O 复用机 ...

  3. ACE_linux:Reactor与Proactor两种模式的区别

    一.概念: Reactor与Proactor两种模式的区别.这里我们只关注read操作,因为write操作也是差不多的.下面是Reactor的做法: 某个事件处理器宣称它对某个socket上的读事件很 ...

  4. 多线程 Worker Thread 模式

    Worker是“工人”的意思,worker thread pattern中,工人线程(worker thread)会一次抓一件工作来处理,当没有工作可做时,工人线程会停下来等待心得工作过来. Work ...

  5. java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-【费元星Q9715234】

    java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-[费元星Q9715234] 说明如下,不懂的问题直接我[费元星Q9715234] 1.反射的意义在于不将xml tag ...

  6. Reactor模型-多线程程版

    1.概述 在Reactor单线程版本的设计中,I/O任务乃至业务逻辑都由Reactor线程来完成,这无疑增加了Reactor线程的负担,高负载情况下必然会出现性能瓶颈.此外,对于多处理器的服务器来说, ...

  7. C# 多线程并发锁模式-总结

    开篇: 互斥还是lock Monitor Mutex 模式! Muex Monitor lock AutoEventSet ManualEventSet 后续的 ReaderWriterLock   ...

  8. 多线程设计模式 - Future模式

    Future模式是多线程开发中非常常见的一种设计模式,它的核心思想是异步调用.这类似我们日常生活中的在线购物流程,带在购物网看着一件商品时可以提交表单,当订单完成后就可以在家里等待商品送货上门.或者说 ...

  9. Immutable Object模式 - 多线程

    Immutable Object模式 - 多线程 前言 在多线程编程中,我们常会碰到修改一个对象的值,如果在不加锁的情况下 ,就会出现值不一致的问题,那么有没有一种方式可以不通过加锁的方式也可以保证数 ...

随机推荐

  1. Codeforces Round #688(Div 2) D. Checkpoints

    思路 第一步,先推导1,0,0,--,0,就是1后面跟了n-1个0的时候 所需要的期望步数 封闭式推导 \(f_n\)代表从n关开始直接通关需要的步数的期望 n为1的情况,即就只有一个1 \(f_1= ...

  2. Python中使用eval执行下面函数的结果怎么是字符串'10020'?

    定义了函数: def add(a,b): s='a+b' c=compile(s,'','eval') gArea,lArea = {},{} gArea['a']=str(a10) gArea['b ...

  3. PyQt(Python+Qt)学习随笔:QAbstractItemView的verticalScrollMode和horizontalScrollMode属性

    老猿Python博文目录 老猿Python博客地址 一.概述 verticalScrollMode和horizontalScrollMode属性用于控制视图如何在垂直方向和水平方向滚动内容.滚动可以按 ...

  4. 软件工程团队作业-Alpha阶段项目复审

    Alpha阶段项目复审 复审人:黄杰 复审总结表 小组的名字和链接 优点 缺点,bug/140字 最终名次 项目 一个普通的团队 https://www.cnblogs.com/clsgghost/p ...

  5. AcWing 362. 区间

    听书上说有贪心 + 数据结构的做法,研究了一下. 朴素贪心 考虑把所有线段按照右端点 \(b\) 从小到大排序,依次考虑每一条线段的要求: 如果已经满足要求则跳过 否则尽量选择靠后的数(因为之后的线段 ...

  6. shell--检查apache是否启动脚本

    #首先我们需要检查apache是否以启动,这里我们用到的说nmap命令,Linux默认情况下是没有安装nmap命令的. #那么我们需要安装下nmap,安装的命令很简单:yum -y install n ...

  7. PHP表白墙网站源码

    PHP表白墙网站源码,可以做校园内的,也可以做校区间的,可封装成APP.告别QQ空间的表白墙吧. 安装简单,上传程序安装,然后设置账号密码,登陆后台切换模板手机PC都要换开启插件访问前台. 安装完成后 ...

  8. Vue-组件化,父组件传子组件常见传值方式

    前言 我们都知道vue核心之一:组件化,vue中万物皆组件,组件化我认为应该来至于模块化的设计思想,比如在模块化开发中,一个模块就是一个实现特定功能的独立的文件,有了模块我们就更方便去阅读代码,更方便 ...

  9. Hive JDBC执行load时无法从本地加载数据

    通过hive-jdcv连接hive server,在应用服务端执行以下命令,报错:Hiver Server节点上找不到data.txt load data local inpath '/home/dw ...

  10. [日常摸鱼]poj2778 DNA Sequence

    这题太神啦 题意:求长度为$n$的不包含给定DNA序列的DNA序列个数,给定的不超过10个 构建出Trie图,用$danger[i]$来表示不能走到$i$,对于DNA序列结尾的结点$danger$设为 ...