Netty 源码解析(七): NioEventLoop 工作流程
原创申明:本文由公众号【猿灯塔】原创,转载请说明出处标注
NioEventLoop 工作流程
@Override
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
// 判断添加任务的线程是否就是当前 EventLoop 中的线程
boolean inEventLoop = inEventLoop();
// 添加任务到之前介绍的 taskQueue 中,
// 如果 taskQueue 满了(默认大小 16),根据我们之前说的,默认的策略是抛出异常
addTask(task);
if (!inEventLoop) {
// 如果不是 NioEventLoop 内部线程提交的 task,那么判断下线程是否已经启动,没有的话,就启动线程
startThread();
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
原来启动 NioEventLoop 中的线程的方法在这里。另外,上节我们说的 register 操作进到了 taskQueue 中,所以它其实是被归类到了非 IO 操作的范畴。
private void startThread() {
if (state == ST_NOT_STARTED) {
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
try {
doStartThread();
} catch (Throwable cause) {
STATE_UPDATER.set(this, ST_NOT_STARTED);
PlatformDependent.throwException(cause);
}
}
}
}
private void doStartThread() {
assert thread == null;
// 这里的 executor 大家是不是有点熟悉的感觉,它就是一开始我们实例化 NioEventLoop 的时候传进来的 ThreadPerTaskExecutor 的实例。它是每次来一个任务,创建一个线程的那种 executor。
// 一旦我们调用它的 execute 方法,它就会创建一个新的线程,所以这里终于会创建 Thread 实例
executor.execute(new Runnable() {
@Override
public void run() {
// 看这里,将 “executor” 中创建的这个线程设置为 NioEventLoop 的线程!!!
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
// 执行 SingleThreadEventExecutor 的 run() 方法,它在 NioEventLoop 中实现了
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
// ... 我们直接忽略掉这里的代码
}
}
});
}
@Override
protected void run() {
// 代码嵌套在 for 循环中
for (;;) {
try {
// selectStrategy 终于要派上用场了
// 它有两个值,一个是 CONTINUE 一个是 SELECT
// 针对这块代码,我们分析一下。
// 1. 如果 taskQueue 不为空,也就是 hasTasks() 返回 true,
// 那么执行一次 selectNow(),该方法不会阻塞
// 2. 如果 hasTasks() 返回 false,那么执行 SelectStrategy.SELECT 分支,
// 进行 select(...),这块是带阻塞的
// 这个很好理解,就是按照是否有任务在排队来决定是否可以进行阻塞
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
// 如果 !hasTasks(),那么进到这个 select 分支,这里 select 带阻塞的
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
// 默认地,ioRatio 的值是 50
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
// 如果 ioRatio 设置为 100,那么先执行 IO 操作,然后在 finally 块中执行 taskQueue 中的任务
try {
// 1. 执行 IO 操作。因为前面 select 以后,可能有些 channel 是需要处理的。
processSelectedKeys();
} finally {
// 2. 执行非 IO 任务,也就是 taskQueue 中的任务
runAllTasks();
}
} else {
// 如果 ioRatio 不是 100,那么根据 IO 操作耗时,限制非 IO 操作耗时
final long ioStartTime = System.nanoTime();
try {
// 执行 IO 操作
processSelectedKeys();
} finally {
// 根据 IO 操作消耗的时间,计算执行非 IO 操作(runAllTasks)可以用多少时间.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}上面这段代码是 NioEventLoop 的核心,这里介绍两点:
- 首先,会根据 hasTasks() 的结果来决定是执行 selectNow() 还是 select(oldWakenUp),这个应该好理解。如果有任务正在等待,那么应该使用无阻塞的 selectNow(),如果没有任务在等待,那么就可以使用带阻塞的 select 操作。
- ioRatio 控制 IO 操作所占的时间比重:
- 如果设置为 100%,那么先执行 IO 操作,然后再执行任务队列中的任务。
- 如果不是 100%,那么先执行 IO 操作,然后执行 taskQueue 中的任务,但是需要控制执行任务的总时间。也就是说,非 IO 操作可以占用的时间,通过 ioRatio 以及这次 IO 操作耗时计算得出。
当然了,实际情况可能是,Channel 实例被 register 到一个已经启动线程的 NioEventLoop 实例中。
365天干货不断微信搜索「猿灯塔」第一时间阅读,回复【资料】【面试】【简历】有我准备的一线大厂面试资料和简历模板
Netty 源码解析(七): NioEventLoop 工作流程的更多相关文章
- 【Netty源码解析】NioEventLoop
上一篇博客[Netty源码学习]EventLoopGroup中我们介绍了EventLoopGroup,实际说来EventLoopGroup是EventLoop的一个集合,EventLoop是一个单线程 ...
- Netty 源码解析(三): Netty 的 Future 和 Promise
今天是猿灯塔“365篇原创计划”第三篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel 当前:Ne ...
- Netty 源码解析(九): connect 过程和 bind 过程分析
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第九篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
- Netty 源码解析(八): 回到 Channel 的 register 操作
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第八篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
- Netty 源码解析(六): Channel 的 register 操作
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第六篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一 ):开始 Netty ...
- Netty 源码解析(五): Netty 的线程池分析
今天是猿灯塔“365篇原创计划”第五篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...
- Netty 源码解析(四): Netty 的 ChannelPipeline
今天是猿灯塔“365篇原创计划”第四篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...
- Netty 源码解析(二):Netty 的 Channel
本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty源码解析(一):开始 当前:Netty 源码解析(二): Netty 的 Channel ...
- Netty源码分析之NioEventLoop(三)—NioEventLoop的执行
前面两篇文章Netty源码分析之NioEventLoop(一)—NioEventLoop的创建与Netty源码分析之NioEventLoop(二)—NioEventLoop的启动中我们对NioEven ...
随机推荐
- Javascript实现万年历(日历表)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- Java实现 洛谷 P1914 小书童——密码
import java.util.Scanner; public class Main { private static Scanner cin; public static void main(St ...
- java实现 洛谷 P1017 进制转换
import java.util.Scanner; public class Main { private static Scanner cin; public static void main(St ...
- 类似-Xms、-Xmn这些参数的含义:
类似-Xms.-Xmn这些参数的含义: 答: 堆内存分配: JVM初始分配的内存由-Xms指定,默认是物理内存的1/64 JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4 默认空余堆内存小于 ...
- https如何进行加密传输
客户端是没有证书的,也就没有公钥和私钥. SSL握手阶段,服务器把证书传输给客户端,同时也就传输了公钥(公钥是证书的一部分). 由客户端来对这个证书进行有效性认可,再由这个客户端来生成对称密钥. 对称 ...
- 完美解决PYQT5登录界面跳转主界面方法
该问题,有很多种方法,但是很多方法要么这个有问题,要么那个有问题,最后终于找到一种没问题的方法.记录一下: Login.py(登录窗口)文件 import sys from PyQt5 import ...
- Android学习笔记样式资源文件
样式资源和主题资源都是写在styles.xml文件里面的 <style name="title"> <item name="android:textSi ...
- [源码解析] GroupReduce,GroupCombine 和 Flink SQL group by
[源码解析] GroupReduce,GroupCombine和Flink SQL group by 目录 [源码解析] GroupReduce,GroupCombine和Flink SQL grou ...
- 《Redis开发与运维》
第1章 初识Redis 1. Redis介绍: Redis是一种基于键值对(key-value)的NoSQL数据库. 与很多键值对数据库不同的是,Redis中的值可以是由string(字符串).has ...
- Domain Adaptive Faster R-CNN:经典域自适应目标检测算法,解决现实中痛点,代码开源 | CVPR2018
论文从理论的角度出发,对目标检测的域自适应问题进行了深入的研究,基于H-divergence的对抗训练提出了DA Faster R-CNN,从图片级和实例级两种角度进行域对齐,并且加入一致性正则化来学 ...