当前位置: 首页 > news >正文

第二章Netty,EventLoop任务队列的详细代码实现

下面将从核心类定义、队列初始化、任务提交、队列消费全流程,给出EventLoop任务队列的详细代码实现拆解,所有代码均来自Netty官方核心源码的精简梳理:

一,核心源码梳理

一、核心类基础定义

任务队列的核心逻辑定义在SingleThreadEventExecutor中,它是所有单线程事件执行器的父类,NioEventLoop直接继承它的队列能力:

// SingleThreadEventExecutor 核心成员定义publicabstractclassSingleThreadEventExecutorextendsAbstractScheduledEventExecutor{// 事件循环线程状态:0=初始化, 1=运行, 2=关闭privatevolatileintstate=ST_NOT_STARTED;// 普通任务队列:默认使用Netty自研的MPSC无锁队列privatefinalQueue<Runnable>taskQueue;// 事件循环绑定的专属线程privatevolatileThreadthread;// 尾部任务队列:存放低优先级收尾任务privatefinalQueue<Runnable>tailTasks;// 构造方法中完成队列初始化protectedSingleThreadEventExecutor(EventLoopGroupparent,Executorexecutor,booleanaddTaskWakesUp,intmaxPendingTasks,RejectedExecutionHandlerrejectedHandler){super(parent);// 初始化MPSC无锁队列,多线程并发提交无需加锁this.taskQueue=newMpscLinkedQueue<>();this.tailTasks=newMpscLinkedQueue<>();// 配置任务拒绝策略this.rejectedExecutionHandler=ObjectUtil.checkNotNull(rejectedHandler,"rejectedHandler");}}

二、NioEventLoop中的队列初始化

NioEventLoop在实例化时,会直接复用父类的队列能力,同时绑定自己的Selector实例:

publicfinalclassNioEventLoopextendsSingleThreadEventLoop{// NIO Selector 实例,用于监听IO就绪事件privatefinalSelectorselector;// 优化迭代性能的选中键集合privateSelectedSelectionKeySetselectedKeys;NioEventLoop(NioEventLoopGroupparent,Executorexecutor,SelectorProviderselectorProvider,SelectStrategyselectStrategy){super(parent,executor,false,DEFAULT_MAX_PENDING_TASKS,RejectedExecutionHandlers.reject());// 打开并初始化JDK NIO Selectorthis.selector=SelectorUtil.open(selectorProvider);this.selectStrategy=ObjectUtil.checkNotNull(selectStrategy,"selectStrategy");}}

三、任务提交的核心实现

外部线程调用execute()提交任务时,会先做线程身份校验,再完成入队操作:

@Overridepublicvoidexecute(Runnabletask){ObjectUtil.checkNotNull(task,"task");// 任务状态校验,拒绝已关闭状态下的新任务if(isShutdown()){reject(task);}else{// 如果当前线程就是EventLoop绑定的IO线程,直接执行无需入队if(inEventLoop()){task.run();}else{// 外部线程提交任务,直接追加到MPSC队列队尾taskQueue.add(task);// 唤醒正在Selector上阻塞的IO线程,及时处理新任务if(!addTaskWakesUp&&wakesUpForTask(task)){wakeup(inEventLoop());}}}}

四、队列消费的核心流程

队列消费完全融入NioEventLoop的无限事件循环中,通过ioRatio参数动态控制IO事件和任务执行的时间占比:

@Overrideprotectedvoidrun(){for(;;){try{booleanoldWakenUp=wakenUp.getAndSet(false);// 根据队列是否有任务动态调整select阻塞时长intselectCnt=0;longcurrentTimeNanos=System.nanoTime();longselectDeadlineNanos=currentTimeNanos+delayNanos(currentTimeNanos);intselectedKeys=select(selectDeadlineNanos);// 处理所有就绪的IO事件if(selectedKeys>0){processSelectedKeys();}finalintioRatio=this.ioRatio;if(ioRatio==100){// IO占比100%场景,处理完IO后执行所有队列任务runAllTasks();}else{// 统计IO处理耗时,按比例分配任务执行时间finallongioStartTime=System.nanoTime();processSelectedKeys();finallongioTime=System.nanoTime()-ioStartTime;// 按ioRatio配额执行任务,避免任务长时间抢占IO资源runAllTasks(ioTime*(100-ioRatio)/ioRatio);}}catch(Throwablet){handleLoopException(t);}// 优雅关闭流程:执行完队列剩余所有任务后退出循环if(isShuttingDown()){closeAll();if(confirmShutdown()){return;}}}}

五、runAllTasks 任务批量执行实现

该方法会在指定时间配额内,批量从队列中取出任务执行,同时处理定时任务和尾部任务:

protectedbooleanrunAllTasks(longtimeoutNanos){// 先将到期的定时任务从定时队列转移到普通任务队列fetchScheduledTasks();Runnabletask;longdeadline=System.nanoTime()+timeoutNanos;// 在时间配额内循环取出普通任务执行while((task=taskQueue.poll())!=null){task.run();// 超过时间配额后停止执行任务,返回继续处理IO事件if(System.nanoTime()-deadline>=0){break;}}// 执行所有尾部任务runAllTasksFromTail(tailTasks);return!taskQueue.isEmpty();}

二,EventLoop的任务队列实现详解

EventLoop的任务队列是Netty实现单线程串行化、局部无锁化高性能设计的核心组件,它的实现完全围绕“多线程安全提交、单线程串行消费”的核心目标设计,具体实现细节可以拆解为以下几个部分:

一、队列的底层架构基础

每个EventLoop的任务队列都定义在父类SingleThreadEventExecutor中,和EventLoop的绑定线程一一对应,从根源上保证了队列的专属独立性:

普通任务队列(taskQueue)‌

默认采用Netty自研的JCTools MPSC无锁队列(多生产者单消费者队列)实现,完全摒弃了传统阻塞队列的重入锁机制,在多业务线程并发提交任务时,不需要加锁就能保证线程安全,性能远高于JDK原生的ArrayBlockingQueue等阻塞队列。

定时任务队列(scheduledTaskQueue)‌

基于优先队列实现,会自动按照任务的执行时间戳排序,确保到期的任务能被优先调度,同时Netty还配套了HashedWheelTimer时间轮组件,针对大量长周期定时任务场景做了性能优化,避免优先队列排序的性能损耗。

尾部任务队列(tailTasks)‌

是一个轻量的普通队列,优先级最低,仅在每一轮事件循环的所有核心任务执行完成后才会被处理,专门用于存放监控统计、资源清理这类非核心收尾任务。

二、任务提交的实现逻辑

当外部线程调用execute()/schedule()方法提交任务时,Netty会先做线程身份校验,避免不必要的入队开销:

如果当前提交任务的线程,正好是EventLoop绑定的专属IO线程,短任务可以直接执行,不需要入队排队
如果是其他外部线程提交任务,任务会被直接追加到对应队列的队尾,整个入队过程无锁,不会阻塞提交方线程
队列默认设置了容量上限,当任务堆积超过阈值时,会直接抛出RejectedExecutionException拒绝新任务,避免无限制的内存溢出风险。

三、队列消费的运行机制

任务队列的消费完全融入NioEventLoop的核心事件循环流程中,通过ioRatio参数动态控制IO事件和队列任务的执行时间占比:

每一轮循环先调用Selector的select方法,阻塞等待IO就绪事件,等待时长会根据队列是否有任务动态调整,避免长时间阻塞导致队列任务无法及时响应
处理完所有就绪的IO事件后,会按照预先设定的时间配额,批量从队列中取出任务执行,保证IO事件不会被队列任务长时间抢占资源
任务执行严格遵循FIFO先进先出规则,保证同一个Channel的所有操作按提交顺序串行执行,从根源上避免多线程乱序导致的数据异常。

四、关键优化设计

局部无锁化‌:所有任务最终都只会在EventLoop的单条专属线程中执行,队列仅在多生产者入队时做无锁并发控制,消费侧完全没有锁竞争,大幅降低了并发场景下的性能损耗。
优雅关闭兜底‌:当EventLoop触发优雅关闭流程时,会先停止接收新任务,然后把队列中剩余的未完成任务全部执行完毕,再终止绑定线程,避免出现任务丢失的情况。
非空快速唤醒‌:当队列从空变为非空时,会主动唤醒正在Selector上阻塞等待的IO线程,让新提交的任务可以被及时处理,不需要等到select阻塞超时。

http://www.jsqmd.com/news/1140601/

相关文章:

  • IIM-20670与PIC18F86J15的高精度运动感知系统设计
  • 100万+随机个性签名免费API接口教程
  • SVD 与 PCA 实战对比:3 种降维场景下的 Python 代码与效果差异
  • TradingView筛选器有哪些?一文看懂多资产筛选工具
  • 2000亿设备更新资金全部下达!大规模量产项目的设备选型逻辑正在被重构
  • 土木工程论文降AI工具免费推荐:2026年土木工程毕业论文降AI99.26%达标知网4.8元指南
  • AI 辅助 DeFi 策略生成:从自然语言描述到可安全部署合约的完整链路
  • 如何快速掌握Blender 3MF插件:免费实现3D打印一体化工作流
  • 如何将网页视频保存到本地:免费开源插件的完整使用指南
  • Navicat重置脚本终极指南:3种方法轻松解除Mac版14天试用限制
  • 收藏!小白程序员必看:大模型如何学会“学习”不再是死记硬背
  • 如何永久保存微信聊天记录:WeChatMsg完全指南实现数据自主管理
  • 2026年万方AIGC检测超标怎么办:万方检测4.8元一次过完整应对方案
  • 鞍山老人怎么吃得省心又方便?靠谱餐食配送源头厂家推荐指南
  • eNSP避坑指南(一)-Router路由器
  • 2026年文档共享管理工具有哪些?8款高效协作利器大盘点
  • Windows右键菜单太乱了?3步教你用ContextMenuManager轻松管理
  • 3分钟掌握AKShare:从连接中断到稳定获取金融数据的完整指南
  • Windows系统文件ChxAPDS.dll丢失找不到问题解决
  • 3款主流节假日 API 对比评测:ThinkAPI vs 极速数据 vs GitHub 开源方案
  • 投了简历没回应?用这个AI做简历优化,HR眼前一亮
  • Switch上的B站全新方案:wiliwili深度体验指南
  • 3分钟搞定:让经典游戏在现代Windows重获新生的终极方案
  • 如何快速搭建社交媒体数据采集系统:面向初学者的完整指南
  • 如何在Windows 11上玩经典游戏:IPXWrapper终极兼容方案指南
  • BMI160传感器与PIC18F4682的低功耗运动监测方案
  • Windows下ClamAV本地部署:TCP Socket外部访问实战指南
  • kbuild-standalone未来展望:构建工具独立化的完整路线图与即将推出的新功能
  • IIM-20670运动传感器与MK60微控制器的SPI通信与数据融合
  • IPXWrapper终极指南:三步让经典游戏在Windows 10/11重获联机能力