Netty源码分析第二章:NioEventLoop

 

第三节:初始化线程选择器

回到上一小节的MultithreadEventExecutorGroup类的构造方法:

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
//代码省略
if (executor == null) {
//创建一个新的线程执行器(1)
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//构造NioEventLoop(2)
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
//代码省略
}
}
//创建线程选择器(3)
chooser = chooserFactory.newChooser(children);
//代码省略
}

我们看第三步, 创建线程选择器:

chooser = chooserFactory.newChooser(children);

NioEventLoop都绑定一个chooser对象, 作为线程选择器, 通过这个线程选择器, 为每一个channel分配不同的线程

我们看到newChooser(children)传入了NioEventLoop数组

我们跟到DefaultEventExecutorChooserFactory类中的newChooser方法:

public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
return new PowerOfTowEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}

这里通过 isPowerOfTwo(executors.length) 判断NioEventLoop的线程数是不是2的倍数, 然后根据判断结果返回两种选择器对象, 这里使用到java设计模式的策略模式

根据这两个类的名字不难看出, 如果是2的倍数, 使用的是一种高性能的方式选择线程, 如果不是2的倍数, 则使用一种比较普通的线程选择方式

我们简单跟进这两种策略的选择器对象中看一下, 首先看一下PowerOfTowEventExecutorChooser这个类:

private static final class PowerOfTowEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
PowerOfTowEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
}

这个类实现了线程选择器的接口EventExecutorChooser, 构造方法中初始化了NioEventLoop线程数组

重点关注下next()方法, next()方法就是选择下一个线程的方法, 如果线程数是2的倍数, 这里通过按位与进行计算, 所以效率极高

再看一下GenericEventExecutorChooser这个类:

private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}

这个类同样实现了线程选择器的接口EventExecutorChooser, 并在造方法中初始化了NioEventLoop线程数组

再看这个类的next()方法, 如果线程数不是2的倍数, 则用绝对值和取模的这种效率一般的方式进行线程选择

这样, 我们就初始化了线程选择器对象

上一节: NioEventLoopGroup之NioEventLoop的创建

下一节: NioEventLoop线程启动

Netty源码分析第2章(NioEventLoop)---->第3节: 初始化线程选择器的更多相关文章

  1. Netty源码分析第2章(NioEventLoop)---->第1节: NioEventLoopGroup之创建线程执行器

    Netty源码分析第二章: NioEventLoop 概述: 通过上一章的学习, 我们了解了Server启动的大致流程, 有很多组件与模块并没有细讲, 从这个章开始, 我们开始详细剖析netty的各个 ...

  2. Netty源码分析第2章(NioEventLoop)---->第2节: NioEventLoopGroup之NioEventLoop的创建

    Netty源码分析第二章: NioEventLoop   第二节: NioEventLoopGroup之NioEventLoop的创建 回到上一小节的MultithreadEventExecutorG ...

  3. Netty源码分析第2章(NioEventLoop)---->第4节: NioEventLoop线程的启动

    Netty源码分析第二章: NioEventLoop   第四节: NioEventLoop线程的启动 之前的小节我们学习了NioEventLoop的创建以及线程分配器的初始化, 那么NioEvent ...

  4. Netty源码分析第2章(NioEventLoop)---->第5节: 优化selector

    Netty源码分析第二章: NioEventLoop   第五节: 优化selector 在剖析selector轮询之前, 我们先讲解一下selector的创建过程 回顾之前的小节, 在创建NioEv ...

  5. Netty源码分析第2章(NioEventLoop)---->第6节: 执行select操作

    Netty源码分析第二章: NioEventLoop   第六节: 执行select操作 分析完了selector的创建和优化的过程, 这一小节分析select相关操作 跟到跟到select操作的入口 ...

  6. Netty源码分析第2章(NioEventLoop)---->第7节: 处理IO事件

    Netty源码分析第二章: NioEventLoop   第七节:处理IO事件 上一小节我们了解了执行select()操作的相关逻辑, 这一小节我们继续学习select()之后, 轮询到io事件的相关 ...

  7. Netty源码分析第2章(NioEventLoop)---->第8节: 执行任务队列

      Netty源码分析第二章: NioEventLoop   第八节: 执行任务队列 继续回到NioEventLoop的run()方法: protected void run() { for (;;) ...

  8. Netty源码分析第4章(pipeline)---->第7节: 前章节内容回顾

    Netty源码分析第四章: pipeline 第七节: 前章节内容回顾 我们在第一章和第三章中, 遗留了很多有关事件传输的相关逻辑, 这里带大家一一回顾 首先看两个问题: 1.在客户端接入的时候, N ...

  9. Netty源码分析第5章(ByteBuf)---->第10节: SocketChannel读取数据过程

    Netty源码分析第五章: ByteBuf 第十节: SocketChannel读取数据过程 我们第三章分析过客户端接入的流程, 这一小节带大家剖析客户端发送数据, Server读取数据的流程: 首先 ...

随机推荐

  1. UVa 10214 - Trees in a Wood.(欧拉函数)

    链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  2. FWT背板笔记

    板子 背板子.jpg \(Fwt\)用于解决这样的问题 \[C_i=\sum_{j\bigoplus k=i}A_j\times B_k\] 其中\(\bigoplus\)是一种二元运算符,如\(or ...

  3. 【洛谷】【搜索(dfs)】P1363 幻想迷宫

    [题目描述:] 幻象迷宫可以认为是无限大的,不过它由若干个N*M的矩阵重复组成.矩阵中有的地方是道路,用'.'表示:有的地方是墙,用'#'表示.LHX和WD所在的位置用'S'表示.也就是对于迷宫中的一 ...

  4. ZooKeeper学习之路 (十)Hadoop的HA集群的机架感知

    一.背景 Hadoop 的设计目的:解决海量大文件的处理问题,主要指大数据的存储和计算问题,其中, HDFS 解决数据的存储问题:MapReduce 解决数据的计算问题 Hadoop 的设计考虑:设计 ...

  5. TortoiseGit需要重复填写用户名和密码的问题

    命令行执行: git config --global credential.helper store 即可

  6. request请求转换成对象。

    1)前端post数据过来,key和val键值对会有很多,这个时候往后端进行插值的时候,最好将这些键值对转换成对象进行处理. 使用common-beanutils 来将前端传递过来的map直接转换成对象 ...

  7. summery 总结篇 访问对象属性的方法

    访问一个对象的属性有两种方法: (1),通过“.”来访问:object.propertyName; (2),通过[]来访问:object[propertyName]; 访问一个对象的方法只能通过“.” ...

  8. Notes 20180508 : Java基本程序设计结构之关键字与标识符

    我们成功书写了HelloWorld后,又深入了解了main函数,提到过main并非是关键字,可什么又是关键字呢?这其实就是这章要研究的内容,本节研究关键字与标识符,在标识符中我们也会讲解一下Java中 ...

  9. jQuery 事件函数传参异常identifier starts immediately after numeric literal

    问题情境: var arr=[aabbcc,112233]; var html = ""; for(var i =0;i<arr.length;i++){ html += ' ...

  10. Redis全方位详解--数据类型使用场景和redis分布式锁的正确姿势

    一.Redis数据类型 1.string string是Redis的最基本数据类型,一个key对应一个value,每个value最大可存储512M.string一半用来存图片或者序列化的数据. 2.h ...