Netty源码分析---SingleThreadEventLoop与EventLoop关系详解

SpringBoot+Vue+MySQL 高校学科竞赛平台 完整可运行版(源码+文档) 本项目基于SpringBoot+Vue+MySQL构建高校学科竞赛管理平台,覆盖学生、教师、领队教师、管理员四大角色,实现竞赛发布、报名、刷题、评分、公示等全流程闭环管理。系统架构清晰,技术主流,配套完整源码、数据库脚本、万字论文、开题报告、答辩PPT及部署文档,支持一键运行,适用于毕业设计、课程设计及院系二次开发,真正实现“开箱即用”。 阅读详情

SingleThreadEventLoop 与 EventLoop 关系详解

在 NettyServer 中调用 b.bind(port).sync() 后,会来到:

// SingleThreadEventLoop
@Override 
public ChannelFuture register(final ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    promise.channel().unsafe().register(this, promise);  // 这里的 this 是什么?
    return promise;
}

然后在 AbstractChannel 中:

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    // 这里进行赋值
    AbstractChannel.this.eventLoop = eventLoop;
    
    // 后面调用
    eventLoop.execute(new Runnable() {
        @Override
        public void run() {
            register0(promise);
        }
    });
}

问题:

  1. eventLoop 是 SingleThreadEventLoop 实例吗?
  2. SingleThreadEventLoop 实例是个什么东西?
  3. 为什么后面是 SingleThreadEventLoop 去调用 eventLoop.execute() 方法?

答案详解

1. eventLoop 是 SingleThreadEventLoop 实例吗?

不完全是!准确地说,eventLoop 是 NioEventLoop 实例。

让我们看继承关系:

NioEventLoop 
    ↓ extends
SingleThreadEventLoop 
    ↓ extends
SingleThreadEventExecutor 
    ↓ extends
AbstractScheduledEventExecutor
    ↓ extends
AbstractEventExecutor
    ↓ implements
EventLoop (接口)

关键点:

  • NioEventLoop 是最终的实现类
  • SingleThreadEventLoop 是它的父类
  • EventLoop 是接口

所以:

  • 传入的 this 是 NioEventLoop 实例(因为调用 register 方法的对象是 NioEventLoop)
  • 赋值给 eventLoop 变量的也是 NioEventLoop 实例
  • 但由于多态,它可以用 EventLoop 接口类型来引用

2. SingleThreadEventLoop/NioEventLoop 实例是什么东西?

从代码流程看创建过程
// 第一步:创建 NioEventLoopGroup(线程池)
EventLoopGroup parentGroup = new NioEventLoopGroup();

// 内部会创建多个 NioEventLoop 实例
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, ...) {
    children = new EventExecutor[nThreads];  // 线程数组
    
    for (int i = 0; i < nThreads; i++) {
        // 创建每个 NioEventLoop
        children[i] = newChild(executor, args);
    }
}

// NioEventLoopGroup 中的 newChild 方法
@Override
protected EventLoop newChild(Executor executor, Object... args) {
    return new NioEventLoop(this, executor, selectorProvider, ...);
}
NioEventLoop 的本质

NioEventLoop 是一个"单线程的线程池",它具有以下特点:

  1. 它是一个 Executor(线程池)

    • 可以提交任务执行
    • 内部有一个 taskQueue 用于存放任务
  2. 它只有一个线程

    • 内部有一个 Thread 实例(懒加载,第一次提交任务时创建)
    • 所有任务都在这一个线程中执行
  3. 它有一个 Selector

    • 用于处理 NIO 的 IO 事件
    • 可以注册多个 Channel
  4. 它的核心工作

    protected void run() {
        for (;;) {
            // 1. select 操作,监听 IO 事件
            select(wakenUp.getAndSet(false));
            
            // 2. 处理 IO 事件
            processSelectedKeys();
            
            // 3. 处理任务队列中的任务
            runAllTasks();
        }
    }
    
形象比喻
NioEventLoopGroup(餐厅)
    ├── NioEventLoop 1(服务员1 + 他的工作台)
    │       ├── Thread(服务员本人)
    │       ├── Selector(监控多个桌子的设备)
    │       ├── taskQueue(待办任务清单)
    │       └── 多个 Channel(多张桌子)
    │
    ├── NioEventLoop 2(服务员2 + 他的工作台)
    │       ├── Thread
    │       ├── Selector
    │       ├── taskQueue
    │       └── 多个 Channel
    │
    └── ...

3. 为什么是 SingleThreadEventLoop 去调用 execute() 方法?

代码调用链
// 1. 在 SingleThreadEventLoop 的 register 方法中
@Override 
public ChannelFuture register(final ChannelPromise promise) {
    // this 是 NioEventLoop 实例
    promise.channel().unsafe().register(this, promise);
    return promise;
}

// 2. 进入 AbstractChannel.AbstractUnsafe 的 register 方法
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    // eventLoop 就是上面传入的 this(NioEventLoop 实例)
    AbstractChannel.this.eventLoop = eventLoop;
    
    // 判断当前线程是否是 EventLoop 的线程
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        // 不是,则提交任务到 EventLoop
        eventLoop.execute(new Runnable() {
            @Override
            public void run() {
                register0(promise);
            }
        });
    }
}
为什么要调用 execute()?

这是 Netty 线程模型的核心设计!

原因 1:线程安全

// 当前执行 register 的线程:main 线程(或其他业务线程)
// Channel 绑定的 EventLoop 的线程:NioEventLoop 的线程

// 为了保证线程安全,所有对 Channel 的操作都必须在它绑定的 EventLoop 线程中执行

原因 2:避免并发问题

// 如果不这样做,可能出现:
// - main 线程在操作 Channel
// - EventLoop 线程也在操作 Channel
// - 导致并发问题

// 通过 execute() 提交任务,保证所有操作都在同一个线程中串行执行
execute() 方法做了什么?
// SingleThreadEventExecutor
@Override
public void execute(Runnable task) {
    // 1. 判断当前线程是否是 EventLoop 的线程
    boolean inEventLoop = inEventLoop();
    
    // 2. 将任务添加到任务队列
    addTask(task);
    
    // 3. 如果不是 EventLoop 线程提交的任务
    if (!inEventLoop) {
        // 启动 EventLoop 的线程(如果还没启动)
        startThread();
    }
}

private void startThread() {
    if (state == ST_NOT_STARTED) {
        if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
            doStartThread();
        }
    }
}

private void doStartThread() {
    // 使用 ThreadPerTaskExecutor 创建线程
    executor.execute(new Runnable() {
        @Override
        public void run() {
            // 将新创建的线程设置为 NioEventLoop 的线程
            thread = Thread.currentThread();
            
            // 执行 NioEventLoop 的 run 方法(死循环)
            SingleThreadEventExecutor.this.run();
        }
    });
}

完整流程图

main 线程                          NioEventLoop 线程
   |                                      |
   |--b.bind(port)                        |
   |                                      |
   |--initAndRegister()                   |
   |                                      |
   |--group.register(channel)             |
   |                                      |
   |--next() 选择一个 NioEventLoop         |
   |                                      |
   |--eventLoop.register(promise)         |
   |    (此时 eventLoop 是 NioEventLoop)   |
   |                                      |
   |--unsafe.register(this, promise)      |
   |    (this 是 NioEventLoop 实例)        |
   |                                      |
   |--AbstractChannel.eventLoop = eventLoop|
   |    (将 NioEventLoop 绑定到 Channel)   |
   |                                      |
   |--eventLoop.execute(task)             |
   |    (提交 register0 任务)              |
   |                                      |
   |--startThread()                       |
   |    (启动 NioEventLoop 线程)           |
   |                                      |
   |                                      |--Thread 被创建
   |                                      |
   |                                      |--run() 死循环开始
   |                                      |
   |                                      |--从 taskQueue 取出任务
   |                                      |
   |                                      |--执行 register0(promise)
   |                                      |
   |                                      |--doRegister()
   |                                      |   (JDK 底层注册)
   |                                      |
   |                                      |--pipeline.fireChannelRegistered()
   |                                      |
   |<--promise.setSuccess()---------------|
   |    (通知 main 线程注册完成)            |

关键理解点

1. 类型关系

NioEventLoop eventLoop = ...;

// 以下都是合法的,因为继承关系
EventLoop el1 = eventLoop;              // 接口类型
SingleThreadEventLoop el2 = eventLoop;  // 父类类型
EventExecutor el3 = eventLoop;          // 更上层的接口

2. 为什么叫 SingleThreadEventLoop?

// 因为它是一个"单线程的事件循环"
public class SingleThreadEventLoop {
    private Thread thread;  // 只有一个线程
    
    protected void run() {
        for (;;) {  // 事件循环
            // 处理 IO 事件
            // 处理任务
        }
    }
}

3. execute() 的作用

// 作用1:线程安全
eventLoop.execute(() -> {
    // 这里的代码一定在 EventLoop 的线程中执行
    // 避免了多线程并发问题
});

// 作用2:懒加载线程
// 第一次调用 execute() 时,才会创建 Thread 实例
// 这样可以节省资源

// 作用3:任务队列
// 如果 EventLoop 线程正在处理 IO 事件
// 新提交的任务会进入队列等待
// 保证了任务的顺序执行

总结

  1. eventLoop 是 NioEventLoop 实例,不是 SingleThreadEventLoop 实例(SingleThreadEventLoop 是父类)

  2. NioEventLoop 是一个"单线程的线程池":

    • 有一个 Thread 实例
    • 有一个 Selector 实例
    • 有一个 taskQueue 任务队列
    • 可以处理 IO 事件和普通任务
  3. 调用 execute() 的原因:

    • 保证线程安全(所有操作在同一线程)
    • 懒加载线程(第一次提交任务时创建)
    • 任务队列化(保证顺序执行)
  4. 这是 Netty 的核心设计:

    • 一个 Channel 绑定一个 NioEventLoop
    • 该 Channel 的所有操作都在这个 NioEventLoop 的线程中执行
    • 避免了锁竞争,提高了性能
手把手教你做基于stm32的红外、语音、按键智能灯光控制(上) 这次项目使用的板子是stm32f103c8t6最小系统板,这个板子在tb上都能够买到,随便一个最小系统板都可以,== 注意在买最小系统板的时候需要买一个stlink下载器来下载程序。显示模块是使用的0.96英寸的OLED屏幕,注意是使用的IIC通信协议的OLED屏幕,不是使用SPI总线协议的。这次写的代码还顺便集成了检测温湿度的功能,所以要使用DHT11温湿度传感器,来采集温度。本文所设计的基于单片机的灯光控制系统主要由模式选择功能、手动模式和自动模式组成。LED灯自然就是要控制的部件了。 阅读详情

相关推荐

三相Vienna整流器过零点电流畸变?试试这个SVPWM调制波补偿技巧

本文针对三相Vienna整流器在电流过零点附近出现的电流畸变问题,提出了一种基于SVPWM调制波补偿的有效解决方案。该方法通过注入零序分量重构占空比,巧妙解决因拓扑约束导致的调制矛盾,在基本不增加计算复杂度的前提下,显著平滑电流波形,降低THD。

weixin_29212867的博客 115

三、netty源码分析之EventLoop

一、EventLoop功能概述 上篇我们分析了EventLoopGroup的核心能力,EventLoopGroup具有执行任务、注册Channel、执行器调度等能力。今天我们来看一下EventLoop。我们先来看看EventLoop的类图关系: 我们可以看到,EventLoop接口继承了EventLoopGroup接口。为什么EventLoop要继承EventLoopGroup呢?从上一篇的分析...

feijianke666的博客 818

从零构建:如何用MATLAB工具箱打造你的第一个多体动力学仿真模型

本文详细介绍了如何使用MATLAB的Simscape Multibody工具箱从零开始构建多体动力学仿真模型,特别适合初学者学习倒立摆系统的建模与仿真。文章涵盖环境准备、坐标系设置、物理模型构建、参数化建模及性能优化等关键步骤,帮助工程师快速掌握这一强大的仿真工具。

water的专栏 964

[netty5: EventLoop & EventLoopGroup]-源码分析

在阅读这篇文章前,建议先阅读: [netty5: Executor & EventExecutor]-源码分析 EventLoop 是一个单线程的事件执行器,负责处理 I/O 事件、任务调度及其注册对象的生命周期管理。 SingleThreadEventLoop SingleThreadEventLoop 是一个基于单线程的事件循环执行器,负责执行任务队列和 I/O 事件,通常绑定一个线程处理所有事件和任务,避免并发同步开销。 EventLoopGroup EventLoopGroup 是多个 Even

... 322

告别手绘!用Aseprite+Unity Tilemap,从AI生成到2D游戏地图的保姆级搭建流程

本文详细介绍了如何利用Aseprite和Unity Tilemap从AI生成素材到搭建2D游戏地图的完整流程。涵盖AI生成素材的预处理、图层管理、动画制作,以及Unity中的Tilemap系统配置和性能优化技巧,帮助开发者高效创建像素风游戏地图。

weixin_29015483的博客 584

(保姆教程)Spyder 配置Tensorflow(2.5.0)和keras(2.4.3)

(保姆教程)Spyder 配置Tensorflow(2.5.0)和keras(2.4.3)

qq_43102225的博客 1万+

Netty入门 - EventLoop 源码剖析

EventLoop是 Netty 中的一个接口,继承自。每个EventLoop绑定到一个或多个Channel,并负责处理这些Channel上的所有 I/O 事件。Netty 中的EventLoop由实现,后者又继承自。EventLoop是 Netty 中处理 I/O 事件的核心组件。通过对EventLoop及其具体实现和的源码剖析,我们可以深入理解 Netty 的事件驱动模型和高性能设计。EventLoop通过事件循环不断处理 I/O 事件和任务队列,确保高效的事件处理和资源管理。

qq_33240556的博客 513

【QNX+Android虚拟化方案】01 - 芯片介绍

【QNX+Android虚拟化方案】01 - 芯片介绍

|~~~热爱生活、努力学习的小伙汁~~~| 2949

[编织消息框架][netty源码分析]3 EventLoop 实现类SingleThreadEventLoop职责与实现

eventLoop是基于事件系统机制,主要技术由线程池同队列组成,是由生产/消费者模型设计,那么先搞清楚谁是生产者,消费者内容 SingleThreadEventLoop实现 public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop { p...

weixin_30369041的博客 179

69页完整版智慧港口综合解决方案

智慧港口需求4港机远程操控等自动化改造需求自动化码头AGV无线通信及专用视频系统需求安全生产视频监控类需求港机RCMS及设备管理需求岸边智能理货码头三维全景应用智慧港口解决方案5自动化码头AGV/跨运车/无人集卡无线接入港机远控视觉及冗余无线通信视觉集卡防吊起系统集装箱码头岸边智能理货港机RCMS系统无线接入港机智能化管理(无线高清视频监控、一键报警、远程抄表系统)港口拖轮无线接入岸边溢油监测系统TD-LTE覆盖及业务终端接入港口生产管理系统港口可视化集群调度港口GiNMS综合网络设备智能管理全球作业效率最

zuoan1993的博客 2130

LVGL嵌入式GUI工程化架构与多窗口实践

嵌入式GUI开发中,LVGL作为轻量级图形库,其核心价值在于高效渲染与事件驱动机制。理解LVGL的对象树模型、资源管理机制(如RAW图像、静态字体)和屏幕生命周期,是构建稳定界面的基础。结合FreeRTOS等实时操作系统,可实现低功耗、高响应的交互体验。典型应用场景包括智能电表、HMI人机界面、工业控制面板等对内存敏感、需长期运行的设备。本文以微信式多窗口架构为范例,深入解析LVGL工程化落地的关键路径:分层目录结构、窗口模块封装、状态机式登录逻辑、TabView多页导航及图表/仪表盘动态可视化,覆盖从资源

weixin_36001279的博客 91

netty源码分析(十七)Netty线程模型深度解读与架构设计原则

上次分析到:public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop public ChannelFuture register(final ChannelPromise promise) { ObjectUtil.checkNotNul

wzq6578702的专栏 3631

【Python机器学习】零基础掌握TweedieRegressor广义线性回归模型

本次文章主要介绍了如何使用Tweedie回归模型预测电动汽车的实际续航里程。基于车速、气温和驾驶习惯等多种因素,模型能够准确地预测出电动汽车的续航里程,从而为生产商和消费者提供有用的信息。优点总结,Tweedie回归模型在处理具有多元性和高度散布的数据方面表现出色,特别适用于需要考虑多种影响因素的预测任务。优点名称对应描述说明多元性强可以同时处理多种类型和维度的数据高度可定制允许用户自定义损失函数和正则项对异常值鲁棒能够较好地处理数据中的异常值易于解释参数和预测结果都相对容易解释。

Mr数据杨 1007

Netty线程池原理

Netty线程池原理

lizc_lizc的博客 1358

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Netty NioEventLoop 创建过程源码分析

原文链接:https://wangwei.one/posts/net... 前面 ,我们分析了Netty中的Channel组件,本篇我们来介绍一下与Channel关联的另一个核心的组件 —— EventLoop。 Netty版本:4.1.30 概述 EventLoop定义了Netty的核心抽象,用于处理网络连接生命周期中所有发生的事件。...

weixin_34221276的博客 89

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

netty源码分析(一)

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34121304的博客 116

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 298

信创环境下PageOffice文档控件适配与实践

信创适配做到文档控件这一层,拼的已经不是"有没有国产版本",而是同一个功能在两类终端上是否真的等价。等价的标准很具体:同一次点击能得到同样的结果,同一份公文能得到同样的版式,同一个项目不需要维护两套代码。这背后是三件并不轻松的工作:服务端用一套接口同时支撑两类终端,客户端把两种系统与四种芯片的安装包分发清楚,文档内核层由产品侧跟着 WPS 的版本走。浏览器这一层的压力,则被收进了产品自己的窗口里——升级节奏由产品掌握,不再交给运气。

zi_wu_xian的专栏 327

第三篇 第 6 课

前置:第三篇第 1 课(stdx 引入),第二篇第 1 课(std.regex)。环境:仓颉 SDK 1.2.0。本文所有代码与输出均逐行实测。

weixin_41618057的博客 516

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 351

【从零理解 Spring AI · 12】MCP 的两种角色:做客户端还是做服务端

上一篇讲了 MCP 的概念:标准协议、三大角色、M+N 替代 M×N。

nice_lcj520的博客 541

Java 集合容器高频面试题全解:HashMap 树化阈值、1.7 死循环与 ConcurrentHashMap 演进(2026 招聘 JD 真题复盘)

HashMap 树化阈值 8 的泊松分布依据、退化阈值 6 的防抖设计、JDK 1.7 头插成环死循环的排查与修复、1.8 尾插后为何仍线程不安全、ConcurrentHashMap 放弃分段锁的原因与 ForwardingNode 协作扩容、computeIfAbsent 死锁陷阱、大集合找重复的场景题选型,附三层追问链自测。题目来源为 38 期公开招聘 JD 样本与字节、拼多多、建行、美团、快手公开面经。

蒙眼过河 420

spdlog 同步日志库使用详解

spdlog是一个轻量级 C++ 日志库,支持同步日志、异步日志、多线程日志、日志文件轮转和自定义日志格式。对于嵌入式 Linux 应用开发,我建议先使用。它可以帮我们完成:日志级别过滤。时间戳和线程 ID 输出。日志格式化。终端和文件输出。多线程安全写入。日志文件按大小或时间轮转。我们不需要自己实现互斥锁、文件写入、日志格式化和轮转等底层功能。

zengjiajun2006的博客 1120

JavaSE通关手册:数据类型与变量

代码无需额外处理,编译时编译器自动完成转换。

xys_678的博客 221

Java 多态 学习笔记

多态是面向对象编程的重要特性,指同一操作作用于不同对象时产生不同行为。其核心在于“父类引用指向子类对象”,结合方法重写实现运行时动态绑定。多态使代码更灵活、可扩展,如用Person p = new Student();统一处理各类人员注册,无需修改调用逻辑。但多态无法直接调用子类特有方法,需通过instanceof判断并强制转换。编译时看左边(类型),运行时看右边(实际对象),实现解耦与维护便利。

AA_AA114514的博客 367
上一篇: Netty源码分析---SingleThreadEventExecutor-execute方法详解
下一篇: Netty源码分析---startThread方法详解
不早睡不改名@
博客等级 码龄5年 536粉丝 306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值