[netty源码分析]3 eventLoop 实现类SingleThreadEventLoop职责与实现
eventLoop是基于事件系统机制,主要技术由线程池同队列组成,是由生产/消费者模型设计,那么先搞清楚谁是生产者,消费者内容
SingleThreadEventLoop 实现
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {
private final Queue<Runnable> tailTasks;
@Override
protected void afterRunningAllTasks() {
runAllTasksFrom(tailTasks);
}
}
SingleThreadEventLoop是个抽象类,从实现代码上看出很简单的逻辑边界判断
SingleThreadEventExecutor也是个抽象类,代码量比较大,我们先看重要的成员属性
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
//事件队列
private final Queue<Runnable> taskQueue;
//执行事件线程,可以看出只有一个线程只要用来记录executor的当前线程
private volatile Thread thread;
//主要负责监控该线程的生命周期,提取出当前线程然后用thread记录
private final Executor executor;
//用Atomic*技术记录当前线程状态
private static final AtomicIntegerFieldUpdater<SingleThreadEventExecutor> STATE_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(SingleThreadEventExecutor.class, "state");
}
//启动线程做了比较判断
private void startThread() {
if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
doStartThread();
}
}
}
private void doStartThread() {
executor.execute(new Runnable() {
@Override
public void run() {
//记录当前执行线程
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
//这里调用的是子类,注意子类是死循环不停的执行任务
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
//更改线程结束状态 省略部分代码
for (;;) {
int oldState = STATE_UPDATER.get(SingleThreadEventExecutor.this);
if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
break;
}
}
try {
// 执行未完成任务同 shutdown hooks.
for (;;) {
if (confirmShutdown()) {
break;
}
}
} finally {
try {
//最后清理操作,如 NioEventLoop实现 selector.close();
cleanup();
} finally {
//省略部分代码
}
}
}
}
});
}
protected final boolean runAllTasksFrom(Queue<Runnable> taskQueue) {
Runnable task = pollTaskFrom(taskQueue);
if (task == null) {
return false;
}
for (;;) {
//安全执行任务
safeExecute(task);
//继续执行剩余任务
task = pollTaskFrom(taskQueue);
if (task == null) {
return true;
}
}
}
protected final Runnable pollTaskFrom(Queue<Runnable> taskQueue) {
for (;;) {
Runnable task = taskQueue.poll();
//忽略WAKEUP_TASK类型任务
if (task == WAKEUP_TASK) {
continue;
}
return task;
}
}
protected boolean runAllTasks(long timeoutNanos) {
//先执行周期任务
fetchFromScheduledTaskQueue();
//从taskQueue提一个任务,如果为空执行所有tailTasks
Runnable task = pollTask();
//如果taskQueue没有任务,立即执行子类的tailTasks
if (task == null) {
afterRunningAllTasks();
return false;
}
//计算出超时时间 = 当前 nanoTime + timeoutNanos
final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
long runTasks = 0;
long lastExecutionTime;
for (;;) {
safeExecute(task);
runTasks ++;
//当执行任务次数大于64判断是否超时,防止长时间独占CPU
if ((runTasks & 0x3F) == 0) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
if (lastExecutionTime >= deadline) {
break;
}
}
task = pollTask();
if (task == null) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
break;
}
}
afterRunningAllTasks();
this.lastExecutionTime = lastExecutionTime;
return true;
}
//SingleThreadEventLoop run 实现
public class DefaultEventLoop extends SingleThreadEventLoop { @Override
protected void run() {
for (;;) {
Runnable task = takeTask();
if (task != null) {
task.run();
updateLastExecutionTime();
} if (confirmShutdown()) {
break;
}
}
}
}
我们可以在SingleThreadEventExecutor 两个runAllTasks 方法打上断点,看执行任务时调用逻辑

本人为了搞清楚 taskQueue 同tailTasks 类型任务,在任务入队时打断点,分别为 SingleThreadEventLoop executeAfterEventLoopIteration方法同 SingleThreadEventExecutor offerTask方法

ServerBootstrap[bind address] ->
NioEventLoopGroup [register Channel] -> [ChannelPromise] ->
NioEventLoop [build and push register task]
从调用链可以清晰看出,启动 netty server 绑定生成抽象 Channel 然后l转换成ChannelPromise,再调用注册实现register0
这里用了判断是否为当前线程,如果是不用加入队列马上执行,目前减少上下文切换开削
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
}
总结:
1.SingleThreadEventLoop 任务执行加了超时限制,目的防止当前线程长时间执行任务独占cpu
2.提交任务时做了减少上下文开削优化
3.执行任务优先级 1.周期任务 2.taskQueue 3.tailTasks
目前没有看到任何调用 SingleThreadEventLoop executeAfterEventLoopIteration 方法,估计是扩展处理。
4.用到Atomic*技术解决并发问题,从Executor提取当前线程,把单一线程维护交给Executor
[netty源码分析]3 eventLoop 实现类SingleThreadEventLoop职责与实现的更多相关文章
- [编织消息框架][netty源码分析]3 EventLoop 实现类SingleThreadEventLoop职责与实现
eventLoop是基于事件系统机制,主要技术由线程池同队列组成,是由生产/消费者模型设计,那么先搞清楚谁是生产者,消费者内容 SingleThreadEventLoop 实现 public abst ...
- [编织消息框架][netty源码分析]4 eventLoop 实现类NioEventLoop职责与实现
NioEventLoop 是jdk nio多路处理实现同修复jdk nio的bug 1.NioEventLoop继承SingleThreadEventLoop 重用单线程处理 2.NioEventLo ...
- [编织消息框架][netty源码分析]5 eventLoop 实现类NioEventLoopGroup职责与实现
分析NioEventLoopGroup最主有两个疑问 1.next work如何分配NioEventLoop 2.boss group 与child group 是如何协作运行的 从EventLoop ...
- [编织消息框架][netty源码分析]6 ChannelPipeline 实现类DefaultChannelPipeline职责与实现
ChannelPipeline 负责channel数据进出处理,如数据编解码等.采用拦截思想设计,经过A handler处理后接着交给next handler ChannelPipeline 并不是直 ...
- [编织消息框架][netty源码分析]8 Channel 实现类NioSocketChannel职责与实现
Unsafe是托委访问socket,那么Channel是直接提供给开发者使用的 Channel 主要有两个实现 NioServerSocketChannel同NioSocketChannel 致于其它 ...
- [编织消息框架][netty源码分析]9 Promise 实现类DefaultPromise职责与实现
netty Future是基于jdk Future扩展,以监听完成任务触发执行Promise是对Future修改任务数据DefaultPromise是重要的模板类,其它不同类型实现基本是一层简单的包装 ...
- [编织消息框架][netty源码分析]5 EventLoopGroup 实现类NioEventLoopGroup职责与实现
分析NioEventLoopGroup最主有两个疑问 1.next work如何分配NioEventLoop 2.boss group 与child group 是如何协作运行的 从EventLoop ...
- [编织消息框架][netty源码分析]11 ByteBuf 实现类UnpooledHeapByteBuf职责与实现
每种ByteBuf都有相应的分配器ByteBufAllocator,类似工厂模式.我们先学习UnpooledHeapByteBuf与其对应的分配器UnpooledByteBufAllocator 如何 ...
- [编织消息框架][netty源码分析]7 Unsafe 实现类NioSocketChannelUnsafe职责与实现
Unsafe 是channel的内部接口,从书写跟命名上看是不公开给开发者使用的,直到最后实现NioSocketChannelUnsafe也没有公开出去 public interface Channe ...
随机推荐
- java小题:福尔摩斯的约会
原题地址:https://www.nowcoder.com/pat/6/problem/4040 防止广告嫌疑,原题为: 题目描述 大侦探福尔摩斯接到一张奇怪的字条:"我们约会吧! 3485 ...
- Redis数据类型之字符串String
String类型是Redis中最基本也最简单的一种数据类型 首先演示一些常用的命令 一.SET key value 和GET key SET key value 和 GET key 设置键值和获取值 ...
- 使用spring利用HandlerExceptionResolver实现全局异常捕获
最近一直没有时间更新是因为一直在更新自己使用的框架. 之后会慢慢带来对之前使用的spring+mvc+mybatis的优化. 会使用一些新的特性,实现一些新的功能. 我会尽量分离业务,封装好再拿出来. ...
- sql解析xml
我们有时候需要在sql中解析xml,xml解析sql实例如下: DECLARE @params xml DECLARE @customparams xml = null -- 0.解析输入参数 ...
- JS和CSS中引号的使用
font-family属性值如果是英文可以不加引号,如果是中文按照CSS标准则应该加引号,但不加引号也没关系.比如:font-family:Arial,"宋体"," ...
- 闭包(匿名函数) php
php中的闭包,之前不理解.以前项目中虽然有用到,也是别人怎么用,自己也跟着怎么用,也没具体去看一下,时间长了就忘了,也不知道闭包是怎么回事.今天网上搜集了关于php闭包相关的文章,看了7,8篇,干货 ...
- JS中字符串与数组的一些常用方法
真是恨透了这些类似于substring substr slice 要么长得像,要么就功能相近的方法... 1⃣️string 1.substring(start开始位置的索引,end结束位置索引) 截 ...
- js的几大数据类型
一. js的几大数据类型 数字:浮点数(3.14)+整数(1): 字符串:包括由任意数量字符组成的序列,例如:'a','one': 布尔值:true+false: undefined:当我们试图访问一 ...
- 【代码学习】PHP 正则表达式
一.正则表达式介绍 正则表达式是用于描述字符排列和匹配模式的一种规则,主要用于字符串的匹配.查找.替换.分割等操作 ------------------------------------------ ...
- bzoj4816 [Sdoi2017]数字表格
Description Doris刚刚学习了fibonacci数列.用f[i]表示数列的第i项,那么 f[0]=0 f[1]=1 f[n]=f[n-1]+f[n-2],n>=2 Doris用老师 ...