如何在 Java 中利用 while 循环实现一个简单的基于时间轮算法的定时任务调度流程
可行但仅适用于学习、嵌入式或教学场景;生产环境应优先选用 HashedWheelTimer、ScheduledThreadPoolExecutor 或 Quartz。
在 Ja va 中,用 while 循环模拟时间轮(timing wheel)调度器是可行的。不过,这里必须划个重点:这种实现方式更适合用来理解原理、用于嵌入式轻量场景或者教学演示。如果是在真实的生产环境中,更稳妥的选择是直接使用 HashedWheelTimer(来自 Netty)、ScheduledThreadPoolExecutor 或是功能更全面的 Quartz。
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

理解时间轮的核心结构
时间轮算法的核心,并不是去“实时计算下次执行时间”。它的巧妙之处在于,把时间流切分成一个个固定的刻度(我们称之为 tick),每个刻度都对应着一个任务槽(bucket)。任务会根据其延迟时间,被映射到对应的槽位里。主循环每前进一个 tick,就检查当前槽位,并触发其中所有的任务。
这里有几个关键参数需要把握:
- tickDuration:每个 tick 代表多少毫秒(例如 100ms)。
- ticksPerWheel:时间轮一圈总共有多少格(例如 64 格)。那么,这个时间轮能处理的最大延迟时间跨度就是
tickDuration × ticksPerWheel。 - currentTime:当前轮次的基准时间(毫秒级,通常会对齐到 tickDuration 的整数倍)。
用 while 实现单层时间轮主循环
下面是一个简化但可运行的单层时间轮调度器骨架。为了聚焦于核心流程,它暂时没有处理线程安全、任务过期重试等复杂情况。
public class SimpleTimingWheel {
private final long tickDuration; // 例如 100ms
private final int ticksPerWheel; // 例如 64
private final List[] wheel;
private volatile long currentTime; // 当前 tick 开始时间(毫秒)
@SuppressWarnings("unchecked")
public SimpleTimingWheel(long tickDuration, int ticksPerWheel) {
this.tickDuration = tickDuration;
this.ticksPerWheel = ticksPerWheel;
this.wheel = new List[ticksPerWheel];
for (int i = 0; i < ticksPerWheel; i++) {
this.wheel[i] = new ArrayList<>();
}
// 初始化当前时间,对齐到最近的 tick 边界
this.currentTime = System.currentTimeMillis() - (System.currentTimeMillis() % tickDuration);
}
// 添加延迟任务(此实现仅支持延迟时间小于一圈总时长的任务)
public void addTask(Runnable task, long delayMs) {
if (delayMs < 0 || delayMs >= tickDuration * ticksPerWheel) {
throw new IllegalArgumentException("Delay out of range");
}
long expiration = System.currentTimeMillis() + delayMs;
long tick = (expiration - currentTime) / tickDuration;
int idx = (int) (tick % ticksPerWheel);
wheel[idx].add(task);
}
// 主循环:持续推进时间,每 tick 执行一次
public void start() {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
long now = System.currentTimeMillis();
long expectedTime = currentTime + tickDuration;
// 等待到下一个 tick 的开始时刻(避免 busy-wait)
if (now < expectedTime) {
try {
Thread.sleep(expectedTime - now);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
// 更新 currentTime(严格对齐 tick 边界)
currentTime = expectedTime;
// 触发当前 tick 对应槽位中的所有任务
// 注意:这里为了简化没有处理异常,实际应用中需要在 task.run() 外包裹 try-catch
int idx = (int) ((currentTime / tickDuration) % ticksPerWheel);
List tasks = wheel[idx];
for (Runnable task : tasks) {
task.run();
}
tasks.clear(); // 清空已执行的任务
}
});
t.setDaemon(true);
t.start();
}
}
立即学习“Ja va免费学习笔记(深入)”;
使用示例与注意事项
调用方式非常简单:
SimpleTimingWheel wheel = new SimpleTimingWheel(100, 64);
wheel.addTask(() -> System.out.println("Hello at " + System.currentTimeMillis()), 300); // 300ms 后执行
wheel.addTask(() -> System.out.println("World!"), 800); // 800ms 后执行
wheel.start();
// 主线程保持运行(否则 JVM 会退出)
Thread.sleep(2000);
在使用这个简化模型时,有几点需要特别注意:
- 精度限制:最小的延迟单位就是
tickDuration。比如一个 350ms 的任务,实际上会在第 4 个 tick(400ms)时触发。 - 单圈限制:上述实现只支持延迟时间小于一圈总时长的任务。如果需要处理更大时间跨度的延迟,就需要升级为多层时间轮(例如小时轮、分钟轮、秒轮组合)。
- 线程安全:
addTask()方法和主循环可能不在同一个线程中调用,实际应用中需要加锁(比如synchronized或ReentrantLock)或者使用线程安全的集合。 - 异常隔离:某个任务的执行抛出了异常,不应该中断整个 while 循环。务必在
task.run()外部包裹try-catch进行隔离。
为什么不用 while 做生产级调度?
虽然用 while 循环可以实现调度,但它本质上是一种 busy-wait 或者依赖 sleep 的模型,存在几个明显的短板:
- CPU 占用不可控:尤其是在 sleep 精度较差时,可能导致线程频繁唤醒,浪费 CPU 资源。
- 无法高效管理海量任务:当任务数量成千上万时,简单的链表遍历和清空操作会成为性能瓶颈。
- 缺乏企业级功能:比如任务取消、状态查询、失败重试、动态优先级调整等,这些在简单循环里很难优雅实现。
- 运维支持薄弱:没有 JMX 监控、无法动态调整 tick 参数、缺乏可视化的管理界面。
所以,如果真的要在项目中落地,更建议直接使用成熟的方案,比如 io.netty.util.HashedWheelTimer。它的底层虽然也包含了 while 循环、CAS 和无锁队列的思想,但经过了充分的压力测试和优化,是更可靠的选择。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
如何优化Apache2响应速度
Apache2响应速度优化实操指南 想让你的Apache2服务器跑得更快?这事儿其实有章可循。下面这份实操指南,将从基础到进阶,帮你系统地提升响应速度。记住,所有优化都建立在不变动核心业务逻辑和架构的前提下。 一 基础与系统层面优化 优化得从地基开始。系统层面的几个关键设置,往往能以小成本换来大收益
git多人协作的工作流程【汇总】
多人协作必须禁用直接 push 到 main 分支:PR MR 流程是保障代码质量、自动化测试与冲突预判的核心机制;最佳实践包括语义化分支命名、启用分支保护规则,并规范 rebase 与 merge 的使用场景。 多人协作时,为什么禁止直接 push 到 main 分支? 直接向主分支推送代码,表面
CentOS上如何升级PHPStorm到最新版本
在 CentOS 上升级 PhpStorm 的可选方案 说到在 CentOS 上升级 PhpStorm,其实路径很清晰。核心原则是:优先使用内置更新或 JetBrains Toolbox App 这类自动管理工具,其次才是手动下载安装包覆盖升级。下面,就按推荐顺序,把每种方式的操作步骤和关键要点给你
Atom如何设置自动保存?Atom自动保存功能开启教程
Atom如何设置自动保存?Atom自动保存功能开启教程 如果你还在为Atom的自动保存功能头疼,那很可能踩中了几个常见的“坑”。从1 27版本开始,autosa ve功能已经作为核心特性内置,不再依赖插件。但问题也随之而来:为什么设置了却不见效?答案往往藏在版本、配置层级,或者那些本该被清理的旧插件
如何在CentOS上备份PHPStorm的配置文件
在 CentOS 上备份 PhpStorm 配置文件:完整指南与最佳实践 一、备份前的准备工作 在开始备份 PhpStorm 配置之前,充分的准备工作至关重要。这能有效保障备份数据的完整性与安全性,避免因操作不当导致配置丢失或损坏。 彻底关闭 PhpStorm 应用程序:这是首要且必须的步骤。确保
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

