【Netty源码解读和权威指南】第35篇:Netty时间轮HashedWheelTimer源码解析——百万定时任务的秘密
上一篇【第34篇】Netty Selector优化——为什么比JDK NIO快这么多下一篇【第36篇】Netty时间轮高级应用——10亿级定时任务的工程实践一、为什么需要时间轮方案10万定时任务性能问题JDK Timer极差单线程插入O(nlogn)ScheduledThreadPool一般堆插入O(logn)内存大HashedWheelTimer优秀插入O(1)内存小二、时间轮数据结构tickDuration100ms, ticksPerWheel8 ┌───┬───┬───┬───┬───┬───┬───┬───┐ │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ ← 8个槽位 └───┴───┴───┴───┴───┴───┴───┴───┘ ↑ ↑ 指针(每100ms移动一格) 每个槽位是链表 时间轮周期 8 × 100ms 800ms 指针指向槽0执行该槽链表中的所有任务 指针指向槽1执行该槽链表中的所有任务 ...三、核心源码publicclassHashedWheelTimerimplementsTimer{privatefinalWorkerworkernewWorker();privatefinalHashedWheelBucket[]wheel;// 环形数组privatefinalintmask;// wheel.length - 1用于取模privatefinallongtickDuration;// 每格时间间隔// 添加定时任务 O(1)publicTimeoutnewTimeout(TimerTasktask,longdelay,TimeUnitunit){longdeadlineSystem.nanoTime()unit.toNanos(delay)-startTime;HashedWheelTimeouttimeoutnewHashedWheelTimeout(this,task,deadline);// 计算槽位longcalculateddeadline/tickDuration;timeout.remainingRounds(calculated-tick)/wheel.length;intstopIndex(int)(calculatedmask);// 位运算取模wheel[stopIndex].add(timeout);// O(1)追加returntimeout;}// Worker线程核心循环privatefinalclassWorkerimplementsRunnable{publicvoidrun(){do{longdeadlinewaitForNextTick();// 等待到下一个tickintidx(int)(tickmask);HashedWheelBucketbucketwheel[idx];bucket.expireTimeouts(deadline);// 执行到期任务tick;// 指针前进}while(!shutdown);}}}四、实战实现延迟消息publicclassDelayQueueExample{privatestaticfinalHashedWheelTimertimernewHashedWheelTimer();publicstaticvoidsendDelayed(Stringmsg,longdelayMs){timer.newTimeout(timeout-{System.out.println(延迟消息到达: msg);},delayMs,TimeUnit.MILLISECONDS);}publicstaticvoidmain(String[]args)throwsException{sendDelayed(订单超时取消,5000);sendDelayed(支付超时提醒,10000);Thread.sleep(15000);timer.stop();}}五、优缺点与适用场景特性说明✅ 插入O(1)计算槽位→追加到链表✅ 内存小只需环形数组链表❌ 精度有限受tickDuration影响❌ 不适合低延迟毫秒级精度够用适用心跳检测、超时重试、延迟消息、连接空闲检测上一篇【第34篇】Netty Selector优化——为什么比JDK NIO快这么多下一篇【第36篇】Netty时间轮高级应用——10亿级定时任务的工程实践