refactor: rename files to Chinese and organize by category
Organized 50 interview questions into 12 categories: - 01-分布式系统 (9 files): 分布式事务, 分布式锁, 一致性哈希, CAP理论, etc. - 02-数据库 (2 files): MySQL索引优化, MyBatis核心原理 - 03-缓存 (5 files): Redis数据结构, 缓存问题, LRU算法, etc. - 04-消息队列 (1 file): RocketMQ/Kafka - 05-并发编程 (4 files): 线程池, 设计模式, 限流策略, etc. - 06-JVM (1 file): JVM和垃圾回收 - 07-系统设计 (8 files): 秒杀系统, 短链接, IM, Feed流, etc. - 08-算法与数据结构 (4 files): B+树, 红黑树, 跳表, 时间轮 - 09-网络与安全 (3 files): TCP/IP, 加密安全, 性能优化 - 10-中间件 (4 files): Spring Boot, Nacos, Dubbo, Nginx - 11-运维 (4 files): Kubernetes, CI/CD, Docker, 可观测性 - 12-面试技巧 (1 file): 面试技巧和职业规划 All files renamed to Chinese for better accessibility and organized into categorized folders for easier navigation. Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
845
questions/08-算法与数据结构/时间轮算法.md
Normal file
845
questions/08-算法与数据结构/时间轮算法.md
Normal file
@@ -0,0 +1,845 @@
|
||||
# 时间轮算法
|
||||
|
||||
## 数据结构原理
|
||||
|
||||
### 什么是时间轮?
|
||||
时间轮(Timing Wheel)是一种用于定时任务调度的数据结构,通过环形队列和层级结构实现高效的时间管理。它特别适合处理大量延迟任务和周期性任务,在分布式系统中广泛应用。
|
||||
|
||||
### 时间轮的核心概念
|
||||
|
||||
1. **轮槽(Slot)**:时间轮的基本单位,每个槽代表一个时间片
|
||||
2. **指针(Pointer)**:当前时间槽的指针,顺时针移动
|
||||
3. **任务(Task)**:需要执行的任务,包含执行时间信息
|
||||
4. **层级结构**:多级时间轮处理不同时间跨度的任务
|
||||
|
||||
### 时间轮的工作原理
|
||||
|
||||
1. **任务添加**:根据任务延迟时间计算放入的槽位
|
||||
2. **任务执行**:指针到达槽位时,执行该槽位所有任务
|
||||
3. **指针移动**:每过一个时间片,指针移动到下一个槽位
|
||||
4. **延迟计算**:任务延迟时间 = 当前时间到执行时间的差值
|
||||
|
||||
## 图解说明
|
||||
|
||||
```
|
||||
单层时间轮示例(槽位数=8,时间片=1秒):
|
||||
0 1 2 3 4 5 6 7
|
||||
+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| | | | | | | | |
|
||||
|task1|task2| |task3| | |task4| |
|
||||
| | | | | | | | |
|
||||
+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
↑
|
||||
指针
|
||||
当前时间:0秒
|
||||
```
|
||||
|
||||
### 多层时间轮示例
|
||||
|
||||
```
|
||||
多层时间轮(3层):
|
||||
Layer 3 (1小时/槽): [0][1][2][3][4][5] -> 当前:0
|
||||
Layer 2 (1分钟/槽): [0][1][2][3][4][5][6][7] -> 当前:0
|
||||
Layer 1 (1秒/槽): [0][1][2][3][4][5][6][7] -> 当前:0
|
||||
Layer 0 (100ms/槽): [0][1][2][3][4][5][6][7] -> 当前:0
|
||||
```
|
||||
|
||||
### 任务添加流程
|
||||
|
||||
```
|
||||
添加任务(延迟 350ms):
|
||||
1. Layer 0: 350ms / 100ms = 3.5 -> 放入槽 4
|
||||
2. 如果当前指针 > 槽位,放入上一层
|
||||
3. 继续处理,直到找到合适的层级
|
||||
```
|
||||
|
||||
## Java 代码实现
|
||||
|
||||
### 基础时间轮实现
|
||||
|
||||
```java
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class TimingWheel {
|
||||
private final int slotSize; // 槽位数量
|
||||
private final long timePerSlot; // 每个槽位的时间间隔(毫秒)
|
||||
private final List<TimerTask>[] slots; // 时间轮槽
|
||||
private final AtomicInteger currentSlot; // 当前槽位索引
|
||||
private final ExecutorService executor; // 任务执行器
|
||||
private final TimingWheel overflowWheel; // 上层时间轮
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public TimingWheel(int slotSize, long timePerSlot, ExecutorService executor) {
|
||||
this.slotSize = slotSize;
|
||||
this.timePerSlot = timePerSlot;
|
||||
this.slots = (List<TimerTask>[]) new List[slotSize];
|
||||
this.currentSlot = new AtomicInteger(0);
|
||||
this.executor = executor;
|
||||
this.overflowWheel = null;
|
||||
|
||||
for (int i = 0; i < slotSize; i++) {
|
||||
slots[i] = new ArrayList<>();
|
||||
}
|
||||
|
||||
// 启动时间轮线程
|
||||
new Thread(this::rotate).start();
|
||||
}
|
||||
|
||||
// 添加任务
|
||||
public void addTask(TimerTask task) {
|
||||
if (task.getDelay() <= timePerSlot) {
|
||||
int targetSlot = (currentSlot.get() + (int)(task.getDelay() / timePerSlot)) % slotSize;
|
||||
slots[targetSlot].add(task);
|
||||
} else {
|
||||
// 处理跨槽位任务
|
||||
if (overflowWheel == null) {
|
||||
// 创建上层时间轮
|
||||
overflowWheel = new TimingWheel(slotSize, timePerSlot * slotSize, executor);
|
||||
}
|
||||
overflowWheel.addTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
// 时间轮旋转
|
||||
private void rotate() {
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(timePerSlot);
|
||||
|
||||
// 获取当前槽位
|
||||
int slot = currentSlot.getAndIncrement() % slotSize;
|
||||
|
||||
// 执行槽位中的任务
|
||||
List<TimerTask> tasks = slots[slot];
|
||||
if (!tasks.isEmpty()) {
|
||||
List<TimerTask> taskList = new ArrayList<>(tasks);
|
||||
tasks.clear();
|
||||
|
||||
for (TimerTask task : taskList) {
|
||||
executor.submit(() -> task.execute());
|
||||
}
|
||||
}
|
||||
|
||||
// 处理溢出任务
|
||||
if (overflowWheel != null) {
|
||||
overflowWheel.rotateTasks();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 旋转溢出任务
|
||||
private void rotateTasks() {
|
||||
// 从当前槽位取出的任务
|
||||
}
|
||||
}
|
||||
|
||||
// 定时任务接口
|
||||
interface TimerTask {
|
||||
long getDelay(); // 获取延迟时间(毫秒)
|
||||
void execute(); // 执行任务
|
||||
}
|
||||
|
||||
// 具体任务实现
|
||||
class DelayedTask implements TimerTask {
|
||||
private final long delay;
|
||||
private final Runnable task;
|
||||
|
||||
public DelayedTask(long delay, Runnable task) {
|
||||
this.delay = delay;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 增强型时间轮实现
|
||||
|
||||
```java
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.*;
|
||||
|
||||
public class EnhancedTimingWheel {
|
||||
private final int tier; // 当前层级
|
||||
private final int slotSize; // 槽位数量
|
||||
private final long timePerSlot; // 每个槽位的时间间隔
|
||||
private final long wheelTimeout; // 轮超时时间
|
||||
private final List<TimerTaskEntry>[] slots; // 时间轮槽
|
||||
private final AtomicInteger currentSlot; // 当前槽位索引
|
||||
private final ExecutorService executor; // 任务执行器
|
||||
private final EnhancedTimingWheel overflowWheel; // 上层时间轮
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public EnhancedTimingWheel(int tier, int slotSize, long timePerSlot,
|
||||
ExecutorService executor) {
|
||||
this.tier = tier;
|
||||
this.slotSize = slotSize;
|
||||
this.timePerSlot = timePerSlot;
|
||||
this.wheelTimeout = slotSize * timePerSlot;
|
||||
this.executor = executor;
|
||||
this.slots = (List<TimerTaskEntry>[]) new List[slotSize];
|
||||
this.currentSlot = new AtomicInteger(0);
|
||||
|
||||
for (int i = 0; i < slotSize; i++) {
|
||||
slots[i] = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
if (tier > 0) {
|
||||
this.overflowWheel = new EnhancedTimingWheel(tier - 1, slotSize,
|
||||
timePerSlot * slotSize, executor);
|
||||
} else {
|
||||
this.overflowWheel = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 任务条目
|
||||
public static class TimerTaskEntry {
|
||||
private final TimerTask task;
|
||||
private final long expiration;
|
||||
private TimerTaskEntry next;
|
||||
private volatile boolean cancelled = false;
|
||||
|
||||
public TimerTaskEntry(TimerTask task, long expiration) {
|
||||
this.task = task;
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return expiration <= System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
// 启动时间轮
|
||||
public void start() {
|
||||
if (running.compareAndSet(false, true)) {
|
||||
new Thread(this::rotate).start();
|
||||
}
|
||||
}
|
||||
|
||||
// 添加任务
|
||||
public void addTask(TimerTask task, long delay) {
|
||||
long expiration = System.currentTimeMillis() + delay;
|
||||
TimerTaskEntry entry = new TimerTaskEntry(task, expiration);
|
||||
|
||||
if (delay < wheelTimeout) {
|
||||
int targetSlot = (int)((expiration / timePerSlot) % slotSize);
|
||||
slots[targetSlot].add(entry);
|
||||
} else {
|
||||
if (overflowWheel != null) {
|
||||
overflowWheel.addTask(task, delay);
|
||||
} else {
|
||||
// 延迟太长,直接安排执行
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
task.execute();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 取消任务
|
||||
public boolean cancelTask(TimerTask task) {
|
||||
// 简化的实现,实际需要遍历所有槽位
|
||||
return false;
|
||||
}
|
||||
|
||||
// 时间轮旋转
|
||||
private void rotate() {
|
||||
while (running.get()) {
|
||||
try {
|
||||
Thread.sleep(timePerSlot);
|
||||
|
||||
int slot = currentSlot.getAndIncrement() % slotSize;
|
||||
List<TimerTaskEntry> tasks = slots[slot];
|
||||
|
||||
if (!tasks.isEmpty()) {
|
||||
List<TimerTaskEntry> expiredTasks = new ArrayList<>();
|
||||
List<TimerTaskEntry> activeTasks = new ArrayList<>();
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
for (TimerTaskEntry entry : tasks) {
|
||||
if (entry.isExpired() && !entry.isCancelled()) {
|
||||
expiredTasks.add(entry);
|
||||
} else {
|
||||
activeTasks.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// 清空当前槽位
|
||||
tasks.clear();
|
||||
tasks.addAll(activeTasks);
|
||||
|
||||
// 执行过期任务
|
||||
for (TimerTaskEntry entry : expiredTasks) {
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
entry.task.execute();
|
||||
} catch (Exception e) {
|
||||
// 处理异常
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 处理溢出任务
|
||||
if (overflowWheel != null) {
|
||||
overflowWheel.rotateExpiredTasks();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 旋转过期任务
|
||||
private void rotateExpiredTasks() {
|
||||
// 实现溢出任务的处理
|
||||
}
|
||||
|
||||
// 停止时间轮
|
||||
public void stop() {
|
||||
running.set(false);
|
||||
}
|
||||
|
||||
// 获取统计信息
|
||||
public TimingWheelStats getStats() {
|
||||
TimingWheelStats stats = new TimingWheelStats();
|
||||
stats.tier = tier;
|
||||
stats.slotSize = slotSize;
|
||||
stats.currentSlot = currentSlot.get();
|
||||
stats.totalTasks = Arrays.stream(slots).mapToInt(List::size).sum();
|
||||
return stats;
|
||||
}
|
||||
|
||||
public static class TimingWheelStats {
|
||||
public int tier;
|
||||
public int slotSize;
|
||||
public int currentSlot;
|
||||
public int totalTasks;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 分布式时间轮实现
|
||||
|
||||
```java
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
||||
public class DistributedTimingWheel {
|
||||
private final String nodeId;
|
||||
private final int slotSize;
|
||||
private final long timePerSlot;
|
||||
private final ExecutorService executor;
|
||||
private final Map<String, TimerTaskEntry> taskMap = new ConcurrentHashMap<>();
|
||||
private final TimerWheel[] wheels;
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
|
||||
public DistributedTimingWheel(String nodeId, int slotSize, long timePerSlot,
|
||||
ExecutorService executor) {
|
||||
this.nodeId = nodeId;
|
||||
this.slotSize = slotSize;
|
||||
this.timePerSlot = timePerSlot;
|
||||
this.executor = executor;
|
||||
this.wheels = new TimerWheel[3]; // 3层时间轮
|
||||
|
||||
// 初始化时间轮
|
||||
for (int i = 0; i < 3; i++) {
|
||||
long slotTime = timePerSlot * (long) Math.pow(slotSize, i);
|
||||
wheels[i] = new TimerWheel(i, slotSize, slotTime, executor);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加分布式任务
|
||||
public void addDistributedTask(String taskId, TimerTask task, long delay) {
|
||||
String taskKey = nodeId + ":" + taskId;
|
||||
TimerTaskEntry entry = new TimerTaskEntry(task, System.currentTimeMillis() + delay);
|
||||
taskMap.put(taskKey, entry);
|
||||
|
||||
// 添加到最合适的时间轮
|
||||
for (int i = wheels.length - 1; i >= 0; i--) {
|
||||
if (delay <= wheels[i].getWheelTimeout()) {
|
||||
wheels[i].addTask(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 取消任务
|
||||
public boolean cancelTask(String taskId) {
|
||||
String taskKey = nodeId + ":" + taskId;
|
||||
TimerTaskEntry entry = taskMap.remove(taskKey);
|
||||
if (entry != null) {
|
||||
entry.cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 启动时间轮
|
||||
public void start() {
|
||||
if (running.compareAndSet(false, true)) {
|
||||
for (TimerWheel wheel : wheels) {
|
||||
wheel.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 停止时间轮
|
||||
public void stop() {
|
||||
if (running.compareAndSet(true, false)) {
|
||||
for (TimerWheel wheel : wheels) {
|
||||
wheel.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 内部时间轮类
|
||||
private class TimerWheel {
|
||||
private final int tier;
|
||||
private final int slotSize;
|
||||
private final long timePerSlot;
|
||||
private final long wheelTimeout;
|
||||
private final List<TimerTaskEntry>[] slots;
|
||||
private final AtomicInteger currentSlot;
|
||||
private final ExecutorService executor;
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public TimerWheel(int tier, int slotSize, long timePerSlot,
|
||||
ExecutorService executor) {
|
||||
this.tier = tier;
|
||||
this.slotSize = slotSize;
|
||||
this.timePerSlot = timePerSlot;
|
||||
this.wheelTimeout = slotSize * timePerSlot;
|
||||
this.executor = executor;
|
||||
this.slots = (List<TimerTaskEntry>[]) new List[slotSize];
|
||||
this.currentSlot = new AtomicInteger(0);
|
||||
|
||||
for (int i = 0; i < slotSize; i++) {
|
||||
slots[i] = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
public void addTask(TimerTaskEntry entry) {
|
||||
long remainingDelay = entry.getExpiration() - System.currentTimeMillis();
|
||||
if (remainingDelay <= timePerSlot) {
|
||||
int targetSlot = (int)((entry.getExpiration() / timePerSlot) % slotSize);
|
||||
slots[targetSlot].add(entry);
|
||||
} else {
|
||||
// 转发到更高层级的时间轮
|
||||
if (tier < wheels.length - 1) {
|
||||
wheels[tier + 1].addTask(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (running.compareAndSet(false, true)) {
|
||||
new Thread(this::rotate).start();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
running.set(false);
|
||||
}
|
||||
|
||||
private void rotate() {
|
||||
while (running.get()) {
|
||||
try {
|
||||
Thread.sleep(timePerSlot);
|
||||
int slot = currentSlot.getAndIncrement() % slotSize;
|
||||
|
||||
List<TimerTaskEntry> tasks = slots[slot];
|
||||
if (!tasks.isEmpty()) {
|
||||
List<TimerTaskEntry> expiredTasks = new ArrayList<>();
|
||||
List<TimerTaskEntry> activeTasks = new ArrayList<>();
|
||||
|
||||
for (TimerTaskEntry entry : tasks) {
|
||||
if (entry.isExpired()) {
|
||||
expiredTasks.add(entry);
|
||||
} else {
|
||||
activeTasks.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
tasks.clear();
|
||||
tasks.addAll(activeTasks);
|
||||
|
||||
for (TimerTaskEntry entry : expiredTasks) {
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
entry.getTask().execute();
|
||||
// 从任务映射中移除
|
||||
taskMap.remove(entry.getTaskId());
|
||||
} catch (Exception e) {
|
||||
// 处理异常
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getWheelTimeout() {
|
||||
return wheelTimeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 时间复杂度分析
|
||||
|
||||
### 操作时间复杂度
|
||||
|
||||
| 操作 | 时间复杂度 | 说明 |
|
||||
|------|------------|------|
|
||||
| 添加任务 | O(1) | 直接计算槽位并添加 |
|
||||
| 取消任务 | O(1) | 标记任务为取消状态 |
|
||||
| 执行任务 | O(1) | 指针到达槽位时执行 |
|
||||
| 旋转时间轮 | O(1) | 移动指针,检查槽位 |
|
||||
|
||||
### 空间复杂度
|
||||
|
||||
- O(n) - 存储 n 个任务
|
||||
- 时间轮槽位使用固定空间 O(slotSize)
|
||||
|
||||
### 性能特点
|
||||
|
||||
1. **时间轮旋转**:每次旋转 O(1) 时间复杂度
|
||||
2. **任务执行**:平均每个任务执行 O(1) 时间
|
||||
3. **内存使用**:固定大小的时间轮,内存可控
|
||||
4. **并发性能**:使用 CopyOnWriteArrayList 保证线程安全
|
||||
|
||||
## 实际应用场景
|
||||
|
||||
### 1. 分布式任务调度
|
||||
- **定时任务**:周期性任务执行
|
||||
- **延迟任务**:延迟执行的任务
|
||||
- **任务重试**:失败任务的重试机制
|
||||
|
||||
```java
|
||||
// 分布式任务调度示例
|
||||
public class TaskScheduler {
|
||||
private final DistributedTimingWheel timingWheel;
|
||||
private final ExecutorService executor;
|
||||
|
||||
public void scheduleTask(String taskId, Runnable task, long delay) {
|
||||
timingWheel.addDistributedTask(taskId, () -> {
|
||||
try {
|
||||
task.run();
|
||||
} catch (Exception e) {
|
||||
// 任务执行失败,可以重试
|
||||
scheduleRetry(taskId, task, delay);
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
private void scheduleRetry(String taskId, Runnable task, long delay) {
|
||||
// 实现重试逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 消息队列超时处理
|
||||
- **消息超时**:处理超时的消息
|
||||
- **死信队列**:移除超时未消费的消息
|
||||
- **重试机制**:消息重试调度
|
||||
|
||||
```java
|
||||
// 消息队列超时处理
|
||||
public class MessageQueueTimeoutHandler {
|
||||
private final EnhancedTimingWheel timingWheel;
|
||||
|
||||
public void handleMessageTimeout(String messageId, long timeout) {
|
||||
timingWheel.addTask(() -> {
|
||||
// 处理超时消息
|
||||
handleTimeoutMessage(messageId);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
private void handleTimeoutMessage(String messageId) {
|
||||
// 将消息移到死信队列
|
||||
System.out.println("Message " + messageId + " timed out");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 缓存过期清理
|
||||
- **缓存过期**:清理过期的缓存数据
|
||||
- **惰性删除**:被动删除过期数据
|
||||
- **定时删除**:主动删除过期数据
|
||||
|
||||
```java
|
||||
// 缓存过期清理
|
||||
public class CacheManager {
|
||||
private final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
|
||||
private final EnhancedTimingWheel timingWheel;
|
||||
|
||||
public void put(String key, Object value, long ttl) {
|
||||
CacheEntry entry = new CacheEntry(value, System.currentTimeMillis() + ttl);
|
||||
cache.put(key, entry);
|
||||
|
||||
// 添加到时间轮
|
||||
timingWheel.addTask(() -> {
|
||||
cache.remove(key);
|
||||
}, ttl);
|
||||
}
|
||||
|
||||
private static class CacheEntry {
|
||||
private final Object value;
|
||||
private final long expiration;
|
||||
|
||||
public CacheEntry(Object value, long expiration) {
|
||||
this.value = value;
|
||||
this.expiration = expiration;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 连接池管理
|
||||
- **连接超时**:检测和关闭超时连接
|
||||
- **空闲连接**:清理长时间空闲的连接
|
||||
- **连接保活**:定期检查连接状态
|
||||
|
||||
```java
|
||||
// 连接池管理
|
||||
public class ConnectionPool {
|
||||
private final Map<String, Connection> connections = new ConcurrentHashMap<>();
|
||||
private final EnhancedTimingWheel timingWheel;
|
||||
|
||||
public void addConnection(String id, Connection connection, long timeout) {
|
||||
connections.put(id, connection);
|
||||
|
||||
// 添加超时检测
|
||||
timingWheel.addTask(() -> {
|
||||
Connection conn = connections.get(id);
|
||||
if (conn != null && conn.isIdle()) {
|
||||
conn.close();
|
||||
connections.remove(id);
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. API 限流
|
||||
- **限流窗口**:控制 API 调用频率
|
||||
- **时间窗口**:基于时间窗口的限流
|
||||
- **滑动窗口**:实现滑动限流算法
|
||||
|
||||
```java
|
||||
// API 限流实现
|
||||
public class RateLimiter {
|
||||
private final EnhancedTimingWheel timingWheel;
|
||||
private final Map<String, AtomicInteger> counters = new ConcurrentHashMap<>();
|
||||
|
||||
public boolean allowRequest(String userId, long windowSize, int limit) {
|
||||
String counterKey = userId + ":" + System.currentTimeMillis() / windowSize;
|
||||
AtomicInteger counter = counters.computeIfAbsent(counterKey, k -> new AtomicInteger(0));
|
||||
|
||||
timingWheel.addTask(() -> counters.remove(counterKey), windowSize);
|
||||
|
||||
return counter.incrementAndGet() <= limit;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 与其他调度方式的对比
|
||||
|
||||
| 调度方式 | 时间复杂度 | 内存使用 | 适用场景 | 优点 | 缺点 |
|
||||
|----------|------------|----------|----------|------|------|
|
||||
| 时间轮 | O(1) | O(n) | 大量延迟任务 | 高效、内存可控 | 实现复杂 |
|
||||
| 优先队列 | O(log n) | O(n) | 任务数量较少 | 实现简单 | 性能较差 |
|
||||
| 线程池 | O(1) | O(n) | 短期任务 | 使用简单 | 资源消耗大 |
|
||||
| 定时器 | O(1) | O(1) | 少量任务 | API 简单 | 不适合大量任务 |
|
||||
| NIO | O(1) | O(n) | 高并发 | 性能极高 | 实现复杂 |
|
||||
|
||||
### 时间轮的优势
|
||||
|
||||
1. **高效性**:O(1) 时间复杂度的任务调度
|
||||
2. **内存可控**:固定大小的时间轮结构
|
||||
3. **实时性**:精确的任务执行时间控制
|
||||
4. **扩展性**:支持多层级处理复杂任务
|
||||
5. **并发友好**:线程安全的实现
|
||||
|
||||
### 时间轮的劣势
|
||||
|
||||
1. **实现复杂**:相比其他方式实现较复杂
|
||||
2. **精度限制**:受时间片大小限制
|
||||
3. **内存开销**:需要维护多个时间轮
|
||||
4. **任务取消**:取消任务需要额外处理
|
||||
|
||||
## 常见面试问题
|
||||
|
||||
### Q1: 时间轮和优先队列有什么区别?为什么选择时间轮?
|
||||
**答**:
|
||||
**主要区别**:
|
||||
1. **时间复杂度**:时间轮 O(1),优先队列 O(log n)
|
||||
2. **内存使用**:时间轮固定大小,优先队列动态增长
|
||||
3. **任务处理**:时间轮轮询,优先队列堆排序
|
||||
4. **实现复杂度**:时间轮较复杂,优先队列简单
|
||||
|
||||
**选择时间轮的原因**:
|
||||
- 任务数量大时性能更好
|
||||
- 内存使用更可控
|
||||
- 适合处理大量延迟任务
|
||||
- 支持高并发场景
|
||||
|
||||
### Q2: 如何处理时间轮的精度问题?
|
||||
**答**:
|
||||
**精度优化策略**:
|
||||
1. **调整时间片**:根据需求选择合适的时间片大小
|
||||
2. **多层级时间轮**:小时间片处理短期任务,大时间片处理长期任务
|
||||
3. **实时校准**:定期校准系统时间
|
||||
4. **任务优先级**:高优先级任务单独处理
|
||||
5. **补偿机制**:记录实际执行时间进行补偿
|
||||
|
||||
### Q3: 如何处理时间轮中的任务取消?
|
||||
**答**:
|
||||
**取消机制实现**:
|
||||
1. **标记机制**:在任务条目中设置取消标志
|
||||
2. **垃圾回收**:定期清理已取消的任务
|
||||
3. **主动查询**:提供取消任务的接口
|
||||
4. **批量清理**:在时间轮旋转时批量清理
|
||||
5. **超时自动清理**:长时间未执行的任务自动清理
|
||||
|
||||
```java
|
||||
// 任务取消实现
|
||||
public boolean cancelTask(String taskId) {
|
||||
String taskKey = nodeId + ":" + taskId;
|
||||
TimerTaskEntry entry = taskMap.get(taskKey);
|
||||
if (entry != null) {
|
||||
entry.cancel();
|
||||
// 可以选择立即从任务映射中移除
|
||||
taskMap.remove(taskKey);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
### Q4: 时间轮在分布式环境中如何保证一致性?
|
||||
**答**:
|
||||
**分布式一致性方案**:
|
||||
1. **任务迁移**:任务在节点间迁移时保持一致性
|
||||
2. **时间同步**:所有节点使用同步时钟
|
||||
3. **任务分发**:根据任务类型和节点负载分发任务
|
||||
4. **故障恢复**:节点故障时任务重新分发
|
||||
5. **共识机制**:使用一致性协议保证任务不丢失
|
||||
|
||||
### Q5: 如何优化时间轮的性能?
|
||||
**答**:
|
||||
**性能优化策略**:
|
||||
1. **时间轮层级**:合理设置时间轮层级数量
|
||||
2. **槽位数量**:根据任务分布调整槽位数量
|
||||
3. **任务批处理**:批量处理相似任务
|
||||
4. **线程优化**:使用多线程处理任务执行
|
||||
5. **内存优化**:使用更高效的数据结构
|
||||
6. **缓存优化**:优化热点数据的访问
|
||||
|
||||
### Q6: 时间轮如何处理大量任务?
|
||||
**答**:
|
||||
**大量任务处理方案**:
|
||||
1. **任务分片**:将任务分散到不同时间轮
|
||||
2. **分层处理**:短期任务和长期任务分别处理
|
||||
3. **任务合并**:相似任务合并执行
|
||||
4. **负载均衡**:多个时间轮实例并行工作
|
||||
5. **资源管理**:动态调整时间轮资源分配
|
||||
|
||||
### Q7: 时间轮和事件驱动模式有什么关系?
|
||||
**答**:
|
||||
**关系说明**:
|
||||
1. **结合使用**:时间轮可以作为事件驱动系统的一部分
|
||||
2. **触发机制**:时间轮可以触发事件的执行
|
||||
3. **定时事件**:时间轮专门处理定时事件
|
||||
4. **互补关系**:事件驱动处理即时事件,时间轮处理延迟事件
|
||||
5. **性能协同**:两者结合可以提高系统整体性能
|
||||
|
||||
### Q8: 如何实现任务的优先级处理?
|
||||
**答**:
|
||||
**优先级实现方案**:
|
||||
1. **多个时间轮**:不同优先级使用不同时间轮
|
||||
2. **优先队列**:在时间轮中使用优先队列
|
||||
3. **任务标记**:高优先级任务特殊标记
|
||||
4. **抢占执行**:高优先级任务可以抢占低优先级任务
|
||||
5. **动态调整**:根据任务优先级动态调整执行顺序
|
||||
|
||||
```java
|
||||
// 带优先级的时间轮
|
||||
public class PriorityTimingWheel {
|
||||
private final Map<Integer, EnhancedTimingWheel> priorityWheels = new ConcurrentHashMap<>();
|
||||
|
||||
public void addTask(TimerTask task, long delay, int priority) {
|
||||
EnhancedTimingWheel wheel = priorityWheels.computeIfAbsent(
|
||||
priority,
|
||||
p -> new EnhancedTimingWheel(3, 8, 100, executor)
|
||||
);
|
||||
wheel.addTask(task, delay);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Q9: 时间轮的内存如何管理?
|
||||
**答**:
|
||||
**内存管理策略**:
|
||||
1. **固定大小**:时间轮槽位数量固定
|
||||
2. **对象复用**:复用任务对象减少 GC
|
||||
3. **分代管理**:不同生命周期对象分别管理
|
||||
4. **内存监控**:监控内存使用情况
|
||||
5. **自动扩容**:在必要时自动扩展时间轮
|
||||
|
||||
### Q10: 时间轮的适用场景和限制是什么?
|
||||
**答**:
|
||||
**适用场景**:
|
||||
1. **大量延迟任务**:需要处理大量延迟任务
|
||||
2. **高并发环境**:需要高并发处理能力
|
||||
3. **内存受限环境**:内存使用需要可控
|
||||
4. **实时性要求**:需要精确的时间控制
|
||||
5. **分布式系统**:需要分布式任务调度
|
||||
|
||||
**限制**:
|
||||
1. **精度限制**:受时间片大小限制
|
||||
2. **实现复杂**:相比其他实现较复杂
|
||||
3. **单机限制**:单机处理能力有限
|
||||
4. **任务取消**:取消任务处理复杂
|
||||
5. **时间同步**:分布式环境需要时间同步
|
||||
|
||||
## 总结
|
||||
|
||||
时间轮算法是一种高效的时间调度算法,特别适合处理大量延迟任务和周期性任务。通过合理的设计和优化,可以在各种场景下实现高性能的任务调度。理解时间轮的原理和实现方式,对于设计和实现高性能的分布式系统具有重要意义。
|
||||
Reference in New Issue
Block a user