ScheduledExecutorService
在百万级任务调度时面临巨大挑战。本文将揭示如何基于
时间轮算法
打造高性能定时任务引擎,实现单机百万级任务调度能力,让Spring Boot应用拥有媲美专业调度框架的能力。
一、传统定时任务的性能瓶颈
1.1 传统实现方式
@Scheduled(fixedRate = 5000)public void checkOrderStatus() { // 每5秒检查订单状态}
问题分析:
线程资源浪费:每个任务独占线程
精度不足:最小调度间隔受限(通常≥10ms)
复杂度高:调度10万任务需要10万线程
内存消耗大:每个线程需要1MB栈空间
1.2 性能对比测试

二、时间轮算法深度解析
2.1 时间轮工作原理

核心参数:
tickDuration:槽位时间间隔(如1ms)
ticksPerWheel:槽位数量(如512)
当前指针:指向当前处理的槽位
2.2 分层时间轮
当任务延迟超过轮盘周期时,使用多层时间轮:

三、Spring Boot集成时间轮实战
3.1 核心依赖
<dependency> <groupId>io.netty</groupId> <artifactId>netty-common</artifactId> <version>4.1.94.Final</version> </dependency>
3.2 时间轮封装类
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class HashedWheelScheduler {
private final HashedWheelTimer timer;
public HashedWheelScheduler() {
// 创建时间轮:1ms tick, 512 slots
this.timer = new HashedWheelTimer(
Thread::new,
1,
TimeUnit.MILLISECONDS,
512
);
}
public Timeout schedule(Runnable task, long delay, TimeUnit unit) {
return timer.newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) {
task.run();
}
}, delay, unit);
}
public void scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
timer.newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) {
task.run();
// 重新调度
timer.newTimeout(this, period, unit);
}
}, initialDelay, unit);
}
@PreDestroy
public void shutdown() {
timer.stop();
}
}
3.3 业务任务调度
@Service
public class OrderService {
private final HashedWheelScheduler scheduler;
private final Map<Long, Timeout> timeoutMap = new ConcurrentHashMap<>();
public OrderService(HashedWheelScheduler scheduler) {
this.scheduler = scheduler;
}
public void createOrder(Order order) {
// 30分钟未支付自动取消
Timeout timeout = scheduler.schedule(
() -> cancelUnpaidOrder(order.getId()),
30,
TimeUnit.MINUTES
);
timeoutMap.put(order.getId(), timeout);
}
public void orderPaid(Long orderId) {
// 支付成功后取消定时任务
Timeout timeout = timeoutMap.remove(orderId);
if (timeout != null) {
timeout.cancel();
}
}
private void cancelUnpaidOrder(Long orderId) {
// 取消订单逻辑
timeoutMap.remove(orderId);
}
}
四、高级特性实现
4.1 分布式协调
public class DistributedHashedWheelTimer extends HashedWheelTimer {
private final RedisLock lock;
public DistributedHashedWheelTimer(RedisLock lock) {
this.lock = lock;
}
@Override
public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
if (delay > TimeUnit.MINUTES.toMillis(5)) {
// 长延时任务使用分布式调度
return new DistributedTimeout(task, delay, unit);
}
return super.newTimeout(task, delay, unit);
}
private class DistributedTimeout implements Timeout {
// 实现分布式任务逻辑
}
}
4.2 任务持久化
@Slf4j
public class PersistentTimerTask implements TimerTask {
private final String taskId;
private final Runnable task;
private final TaskRepository repository;
@Override
public void run(Timeout timeout) {
try {
task.run();
repository.markSuccess(taskId);
} catch (Exception e) {
log.error("Task {} failed", taskId, e);
repository.markFailed(taskId, e.getMessage());
}
}
// 从数据库恢复任务
public void recoverTasks() {
List<Task> pendingTasks = repository.findPendingTasks();
for (Task task : pendingTasks) {
long delay = task.getExecuteTime() - System.currentTimeMillis();
if (delay > 0) {
scheduler.schedule(
new PersistentTimerTask(task.getId(), task.getLogic(), repository),
delay,
TimeUnit.MILLISECONDS
);
}
}
}
}
4.3 时间轮监控
@RestController
@RequestMapping("/timing-wheel")
public class TimingWheelMonitor {
@Autowired
private HashedWheelTimer timer;
@GetMapping("/metrics")
public Map<String, Object> getMetrics() {
return Map.of(
"pendingTasks", timer.pendingTimeouts(),
"tickDuration", timer.getTickDuration(),
"wheelSize", timer.getWheelSize()
);
}
@GetMapping("/tasks")
public List<TaskInfo> listPendingTasks() {
// 实现任务列表获取逻辑
}
}
五、性能优化技巧
5.1 时间轮参数调优
public HashedWheelTimer createOptimizedTimer() {
// 根据CPU核心数动态调整
int cores = Runtime.getRuntime().availableProcessors();
int wheelSize = 512;
long tickDuration = 1; // 1ms
if (cores < 4) {
tickDuration = 5; // 5ms
wheelSize = 256;
}
return new HashedWheelTimer(
Executors.defaultThreadFactory(),
tickDuration,
TimeUnit.MILLISECONDS,
wheelSize
);
}
5.2 任务合并技术
public class BatchTimerTask implements TimerTask {
private final List<Runnable> tasks = new ArrayList<>();
public void addTask(Runnable task) {
synchronized (tasks) {
tasks.add(task);
}
}
@Override
public void run(Timeout timeout) {
List<Runnable> toExecute;
synchronized (tasks) {
toExecute = new ArrayList<>(tasks);
tasks.clear();
}
// 并行执行
toExecute.parallelStream().forEach(Runnable::run);
}
}
// 使用示例
BatchTimerTask batchTask = new BatchTimerTask();
timer.newTimeout(batchTask, 100, TimeUnit.MILLISECONDS);
// 添加多个任务
batchTask.addTask(() -> updateCache("key1"));
batchTask.addTask(() -> updateCache("key2"));
5.3 避免任务雪崩
public class ThrottledTaskRunner {
private final Semaphore semaphore = new Semaphore(100); // 最大并发100
public void runWithThrottle(Runnable task) {
if (!semaphore.tryAcquire()) {
// 超过阈值时延迟重试
timer.newTimeout(t -> runWithThrottle(task), 10, TimeUnit.MILLISECONDS);
return;
}
try {
task.run();
} finally {
semaphore.release();
}
}
}
六、典型应用场景
6.1 电商订单超时管理

6.2 金融交易系统
// 国债交易结算
public void scheduleBondSettlement(BondTrade trade) {
// T+1结算
long delay = calculateSettlementDelay(trade.getTradeDate());
scheduler.schedule(() -> {
settlementService.executeSettlement(trade);
// 通知风控系统
riskControlService.reportSettlement(trade.getId());
}, delay, TimeUnit.MILLISECONDS);
}
6.3 游戏服务器
// 玩家技能冷却
public void startSkillCooldown(Player player, Skill skill) {
long cooldown = skill.getCooldownMillis();
scheduler.schedule(() -> {
player.resetSkillCooldown(skill.getId());
// 通知客户端
sendCooldownEnd(player, skill.getId());
}, cooldown, TimeUnit.MILLISECONDS);
}
七、生产环境部署方案
7.1 高可用架构

7.2 配置建议
timing-wheel: tick-duration: 1ms # 时间精度 wheel-size: 512 # 时间轮大小 worker-threads: 4 # 任务执行线程数 max-pending: 1000000 # 最大挂起任务数 recovery: enabled: true # 启用任务恢复 interval: 30s # 恢复间隔
八、源码深度解析
8.1 时间轮核心算法
// 简化版时间轮实现
public class SimpleHashedWheelTimer {
private final long tickDuration;
private final HashedWheelBucket[] wheel;
private volatile int tick;
public SimpleHashedWheelTimer(int ticksPerWheel, long tickDuration) {
this.tickDuration = tickDuration;
this.wheel = new HashedWheelBucket[ticksPerWheel];
// 初始化桶
for (int i = 0; i < ticksPerWheel; i++) {
wheel[i] = new HashedWheelBucket();
}
// 启动工作线程
new Thread(this::run).start();
}
private void run() {
long startTime = System.nanoTime();
while (true) {
long deadline = startTime + (tick + 1) * tickDuration * 1_000_000;
long currentTime = System.nanoTime();
if (currentTime < deadline) {
// 等待下一个tick
LockSupport.parkNanos(deadline - currentTime);
continue;
}
// 处理当前槽位任务
int idx = tick & (wheel.length - 1);
wheel[idx].expireTimeouts();
tick++;
}
}
public void newTimeout(Runnable task, long delay) {
long deadline = System.nanoTime() + delay * 1_000_000;
int ticks = (int) (delay / tickDuration);
int stopIndex = (tick + ticks) & (wheel.length - 1);
wheel[stopIndex].addTimeout(new TimeoutTask(task, deadline));
}
static class HashedWheelBucket {
private final Queue<TimeoutTask> tasks = new ConcurrentLinkedQueue<>();
void addTimeout(TimeoutTask task) {
tasks.offer(task);
}
void expireTimeouts() {
while (!tasks.isEmpty()) {
TimeoutTask task = tasks.poll();
if (task.deadline <= System.nanoTime()) {
task.run();
} else {
// 重新计算位置
// ...
}
}
}
}
static class TimeoutTask implements Runnable {
final Runnable task;
final long deadline;
// 构造方法和run方法...
}
}
8.2 任务哈希算法
protected int calculateTaskHash(long deadline) {
// 避免哈希冲突的优化算法
long duration = deadline - System.nanoTime();
int ticks = (int) (duration / tickDuration);
return (tick + ticks) & (wheel.length - 1);
}
九、总结与展望
9.1 方案优势
极致性能:单机支持百万级定时任务
精准调度:毫秒级调度精度
资源节约:单线程处理所有任务
简单易用:API简洁,学习成本低
无缝集成:与Spring Boot完美融合
9.2 适用场景
大规模延迟任务(订单超时、会话管理)
高精度定时任务(游戏技能冷却、金融交易)
资源敏感型环境(物联网设备、边缘计算)
临时性定时任务(缓存过期、锁释放)
9.3 未来演进
分布式时间轮:集群协同调度
持久化增强:支持任务快照与恢复
动态调整:运行时修改时间轮参数
AI预测调度:基于历史数据的智能调度
时间轮算法作为操作系统级别的定时机制,在Spring Boot中焕发新生。它打破了传统定时任务的性能瓶颈,让普通应用轻松具备处理百万级定时任务的能力,是构建高性能系统的秘密武器。
来源:https://mp.weixin.qq.com/s/YQPEBEZJj9sLHXVHksKzLQ
