├── .idea
├── compiler.xml
├── description.html
├── encodings.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── README.md
├── TimerTask.png
├── TimerTaskList.png
├── TimingWheel.png
├── src
├── Main.java
└── com
│ └── utils
│ ├── TimeWheel.java
│ ├── Timer.java
│ ├── TimerTask.java
│ └── TimerTaskList.java
├── timer.png
├── timing_wheel.iml
└── timing_wheel.png
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/description.html:
--------------------------------------------------------------------------------
1 | Simple Java application that includes a class with main()
method
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 时间轮
2 | 时间轮是一种环形数据结构,类似于时钟,秒针、分针、时针分别为一层,每层分成多个格子,每个格子中存放任务集合,一个单独的线程推进时间一格一格的移动,并执行格子中的任务。
3 | TimingWheel并非简单的环形时间轮,而是多层级时间轮,每个时间轮由多个时间格组成,每个时间格为一个时间间隔,底层的时间格跨度较小,然后随着延迟任务延迟时间的长短逐层变大;
4 | 如下图,下层的时间轮每个时间格为1ms,整个时间轮为10ms,而上面一层的时间轮中时间格为10ms,整个时间轮为100ms,
5 | 上级时间轮添加的规则为:当前currentTime为上级时间轮的startMs,当前interval为上级时间轮的tickDuration,ticksPerWheel相同;
6 | 简单点说就是上层时间轮跨度为当前的M倍,时间格为当前的N倍;
7 | 时间轮常用于延时任务,在Netty、akka、Quartz、Zookeeper等高性能组件中都存在时间轮定时器的踪影。
8 | - 时间轮数据结构
9 |
10 | 
11 |
12 | - 时间轮名词解释
13 |
14 | 时间格:环形结构中用于存放延迟任务的区块
15 | 指针(currentTime):指向当前操作的时间格,代表当前时间
16 | 格数(ticksPerWheel):时间轮中时间格的个数
17 | 间隔(tickDuration):每个时间格之间的间隔
18 | 总间隔(interval):当前时间轮总间隔,也就是等于ticksPerWheel*tickDuration
19 |
20 | ## kafka时间轮
21 | 在Kafka中应用了大量的延迟操作,但在Kafka中 并没用使用JDK自带的Timer或是DelayQueue用于延迟操作,而是使用自己开发的DelayedOperationPurgatory组件用于管理延迟操作,
22 | Kafka这类分布式框架有大量延迟操作并且对性能要求及高,而java.util.Timer与java.util.concurrent.DelayQueue的插入和删除复杂度都为对数阶O(log n)并不能满足Kafka性能要求,
23 | 所以Kafka实现了基于时间轮的定时任务组件,该时间轮定时任务实现的插入与删除(开始定时器与暂停定时器)的时间复杂度都为常数阶O(1)。
24 |
25 | - Timer
26 |
27 | Timer是kafka中的定时器类,定义了共客户端调用的方法。SystemTimer是对Timer的具体实现
28 | taskExecutor:过期任务执行线程,为了不影响性能,过期任务单独开辟线程执行
29 | delayQueue:一个Timer只有一个delayQueue,Timer中所有timingWheel共用,用于获取过期任务
30 | timingWheel:最底层时间轮tickMs(间隔)为1ms,wheelSize(格数)为20
31 |
32 | 
33 |
34 | 关键代码
35 | ```scala
36 | //添加任务
37 | def add(timerTask: TimerTask): Unit = {
38 | readLock.lock()
39 | try {
40 | addTimerTaskEntry(new TimerTaskEntry(timerTask, timerTask.delayMs + Time.SYSTEM.hiResClockMs))
41 | } finally {
42 | readLock.unlock()
43 | }
44 | }
45 | //添加任务,失败直接执行
46 | private def addTimerTaskEntry(timerTaskEntry: TimerTaskEntry): Unit = {
47 | if (!timingWheel.add(timerTaskEntry)) {
48 | if (!timerTaskEntry.cancelled)
49 | taskExecutor.submit(timerTaskEntry.timerTask)
50 | }
51 | }
52 | //获取过期任务,推进时间,任务执行或降轮重入
53 | def advanceClock(timeoutMs: Long): Boolean = {
54 | var bucket = delayQueue.poll(timeoutMs, TimeUnit.MILLISECONDS)
55 | if (bucket != null) {
56 | writeLock.lock()
57 | try {
58 | while (bucket != null) {
59 | timingWheel.advanceClock(bucket.getExpiration())
60 | bucket.flush(reinsert)
61 | bucket = delayQueue.poll()
62 | }
63 | } finally {
64 | writeLock.unlock()
65 | }
66 | true
67 | } else {
68 | false
69 | }
70 | }
71 | ```
72 |
73 | - TimingWheel
74 |
75 | interval:时间轮时间范围tickMs * wheelSize
76 | buckets:TimerTaskList数组,长度对应wheelSize
77 | currentTime:当前时间startMs - (startMs % tickMs),取整为tickMs的倍数
78 | overflowWheel:上级时间轮
79 |
80 | 
81 |
82 | 关键代码
83 | ```scala
84 | //添加或获取上级时间轮
85 | private[this] def addOverflowWheel(): Unit = {
86 | synchronized {
87 | if (overflowWheel == null) {
88 | overflowWheel = new TimingWheel(
89 | tickMs = interval,
90 | wheelSize = wheelSize,
91 | startMs = currentTime,
92 | taskCounter = taskCounter,
93 | queue
94 | )
95 | }
96 | }
97 | }
98 | //添加任务 失败返回false直接执行
99 | def add(timerTaskEntry: TimerTaskEntry): Boolean = {
100 | val expiration = timerTaskEntry.expirationMs
101 | if (timerTaskEntry.cancelled) {
102 | //取消
103 | false
104 | } else if (expiration < currentTime + tickMs) {
105 | //已过期
106 | false
107 | } else if (expiration < currentTime + interval) {
108 | //当前时间轮可以容纳该任务
109 | val virtualId = expiration / tickMs
110 | val bucket = buckets((virtualId % wheelSize.toLong).toInt)
111 | bucket.add(timerTaskEntry)
112 | if (bucket.setExpiration(virtualId * tickMs)) {
113 | queue.offer(bucket)
114 | }
115 | true
116 | } else {
117 | //加入上级时间轮
118 | if (overflowWheel == null) addOverflowWheel()
119 | overflowWheel.add(timerTaskEntry)
120 | }
121 | }
122 | //推进时间
123 | def advanceClock(timeMs: Long): Unit = {
124 | if (timeMs >= currentTime + tickMs) {
125 | currentTime = timeMs - (timeMs % tickMs)
126 | //推进上级时间轮时间
127 | if (overflowWheel != null) overflowWheel.advanceClock(currentTime)
128 | }
129 | }
130 | ```
131 |
132 | - TimerTaskList
133 |
134 | TimerTaskEntry:用于封装TimerTask
135 |
136 | 
137 |
138 | - TimerTask
139 |
140 | trait TimerTask extends Runnable继承java Runnable接口
141 |
142 | 
143 |
144 |
--------------------------------------------------------------------------------
/TimerTask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Xun-Zhou/timing_wheel/a9a3223e9f8d5d6f3ca5e067251159e98b59fd3c/TimerTask.png
--------------------------------------------------------------------------------
/TimerTaskList.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Xun-Zhou/timing_wheel/a9a3223e9f8d5d6f3ca5e067251159e98b59fd3c/TimerTaskList.png
--------------------------------------------------------------------------------
/TimingWheel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Xun-Zhou/timing_wheel/a9a3223e9f8d5d6f3ca5e067251159e98b59fd3c/TimingWheel.png
--------------------------------------------------------------------------------
/src/Main.java:
--------------------------------------------------------------------------------
1 | import com.utils.Timer;
2 | import com.utils.TimerTask;
3 |
4 | import java.util.concurrent.CountDownLatch;
5 |
6 | public class Main {
7 |
8 | static int inCount = 0;
9 |
10 | static int runCount = 0;
11 |
12 | public static void main(String[] args) {
13 | CountDownLatch countDownLatch = new CountDownLatch(1000);
14 | Timer timer = new Timer();
15 | for(int i=1;i<=1000;i++){
16 | TimerTask timerTask = new TimerTask(i,()->{
17 | countDownLatch.countDown();
18 | int index = addRun();
19 | System.out.println(index+"----------执行");
20 | });
21 | timer.addTask(timerTask);
22 | System.out.println(i+"++++++++++加入");
23 | inCount++;
24 | }
25 | TimerTask timerTask = new TimerTask(5000,()->{
26 | countDownLatch.countDown();
27 | int index = addRun();
28 | System.out.println(index+"----------执行");
29 | });
30 | timer.addTask(timerTask);
31 | try {
32 | countDownLatch.await();
33 | System.out.println("inCount" + inCount);
34 | System.out.println("runCount" + runCount);
35 | } catch (Exception e){
36 | e.printStackTrace();
37 | }
38 | }
39 |
40 | public synchronized static int addRun(){
41 | runCount++;
42 | return runCount;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/com/utils/TimeWheel.java:
--------------------------------------------------------------------------------
1 | package com.utils;
2 |
3 | import java.util.concurrent.DelayQueue;
4 |
5 | /**
6 | * 时间轮
7 | */
8 | public class TimeWheel {
9 |
10 | /**
11 | * 一个时间槽的范围
12 | */
13 | private long tickMs;
14 |
15 | /**
16 | * 时间轮大小
17 | */
18 | private int wheelSize;
19 |
20 | /**
21 | * 时间跨度
22 | */
23 | private long interval;
24 |
25 | /**
26 | * 时间槽
27 | */
28 | private TimerTaskList[] timerTaskLists;
29 |
30 | /**
31 | * 当前时间
32 | */
33 | private long currentTime;
34 |
35 | /**
36 | * 上层时间轮
37 | */
38 | private volatile TimeWheel overflowWheel;
39 |
40 | /**
41 | * 一个Timer只有一个delayQueue
42 | */
43 | private DelayQueue delayQueue;
44 |
45 | public TimeWheel(long tickMs, int wheelSize, long currentTime, DelayQueue delayQueue) {
46 | this.currentTime = currentTime;
47 | this.tickMs = tickMs;
48 | this.wheelSize = wheelSize;
49 | this.interval = tickMs * wheelSize;
50 | this.timerTaskLists = new TimerTaskList[wheelSize];
51 | //currentTime为tickMs的整数倍 这里做取整操作
52 | this.currentTime = currentTime - (currentTime % tickMs);
53 | this.delayQueue = delayQueue;
54 | for (int i = 0; i < wheelSize; i++) {
55 | timerTaskLists[i] = new TimerTaskList();
56 | }
57 | }
58 |
59 | /**
60 | * 创建或者获取上层时间轮
61 | */
62 | private TimeWheel getOverflowWheel() {
63 | if (overflowWheel == null) {
64 | synchronized (this) {
65 | if (overflowWheel == null) {
66 | overflowWheel = new TimeWheel(interval, wheelSize, currentTime, delayQueue);
67 | }
68 | }
69 | }
70 | return overflowWheel;
71 | }
72 |
73 | /**
74 | * 添加任务到时间轮
75 | */
76 | public boolean addTask(TimerTask timerTask) {
77 | long expiration = timerTask.getDelayMs();
78 | //过期任务直接执行
79 | if (expiration < currentTime + tickMs) {
80 | return false;
81 | } else if (expiration < currentTime + interval) {
82 | //当前时间轮可以容纳该任务 加入时间槽
83 | Long virtualId = expiration / tickMs;
84 | int index = (int) (virtualId % wheelSize);
85 | System.out.println("tickMs:" + tickMs + "------index:" + index + "------expiration:" + expiration);
86 | TimerTaskList timerTaskList = timerTaskLists[index];
87 | timerTaskList.addTask(timerTask);
88 | if (timerTaskList.setExpiration(virtualId * tickMs)) {
89 | //添加到delayQueue中
90 | delayQueue.offer(timerTaskList);
91 | }
92 | } else {
93 | //放到上一层的时间轮
94 | TimeWheel timeWheel = getOverflowWheel();
95 | timeWheel.addTask(timerTask);
96 | }
97 | return true;
98 | }
99 |
100 | /**
101 | * 推进时间
102 | */
103 | public void advanceClock(long timestamp) {
104 | if (timestamp >= currentTime + tickMs) {
105 | currentTime = timestamp - (timestamp % tickMs);
106 | if (overflowWheel != null) {
107 | //推进上层时间轮时间
108 | this.getOverflowWheel().advanceClock(timestamp);
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/com/utils/Timer.java:
--------------------------------------------------------------------------------
1 | package com.utils;
2 |
3 | import java.util.concurrent.DelayQueue;
4 | import java.util.concurrent.ExecutorService;
5 | import java.util.concurrent.Executors;
6 | import java.util.concurrent.TimeUnit;
7 |
8 | /**
9 | * 定时器
10 | */
11 | public class Timer {
12 |
13 | /**
14 | * 底层时间轮
15 | */
16 | private TimeWheel timeWheel;
17 |
18 | /**
19 | * 一个Timer只有一个delayQueue
20 | */
21 | private DelayQueue delayQueue = new DelayQueue<>();
22 |
23 | /**
24 | * 过期任务执行线程
25 | */
26 | private ExecutorService workerThreadPool;
27 |
28 | /**
29 | * 轮询delayQueue获取过期任务线程
30 | */
31 | private ExecutorService bossThreadPool;
32 |
33 | /**
34 | * 构造函数
35 | */
36 | public Timer() {
37 | timeWheel = new TimeWheel(1, 20, System.currentTimeMillis(), delayQueue);
38 | workerThreadPool = Executors.newFixedThreadPool(100);
39 | bossThreadPool = Executors.newFixedThreadPool(1);
40 | //20ms获取一次过期任务
41 | bossThreadPool.submit(() -> {
42 | while (true) {
43 | this.advanceClock(20);
44 | }
45 | });
46 | }
47 |
48 | /**
49 | * 添加任务
50 | */
51 | public void addTask(TimerTask timerTask) {
52 | //添加失败任务直接执行
53 | if (!timeWheel.addTask(timerTask)) {
54 | workerThreadPool.submit(timerTask.getTask());
55 | }
56 | }
57 |
58 | /**
59 | * 获取过期任务
60 | */
61 | private void advanceClock(long timeout) {
62 | try {
63 | TimerTaskList timerTaskList = delayQueue.poll(timeout, TimeUnit.MILLISECONDS);
64 | if (timerTaskList != null) {
65 | //推进时间
66 | timeWheel.advanceClock(timerTaskList.getExpiration());
67 | //执行过期任务(包含降级操作)
68 | timerTaskList.flush(this::addTask);
69 | }
70 | } catch (Exception e) {
71 | e.printStackTrace();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/com/utils/TimerTask.java:
--------------------------------------------------------------------------------
1 | package com.utils;
2 |
3 | /**
4 | * 任务
5 | */
6 | public class TimerTask {
7 |
8 | /**
9 | * 延迟时间
10 | */
11 | private long delayMs;
12 |
13 | /**
14 | * 任务
15 | */
16 | private Runnable task;
17 |
18 | /**
19 | * 时间槽
20 | */
21 | protected TimerTaskList timerTaskList;
22 |
23 | /**
24 | * 下一个节点
25 | */
26 | protected TimerTask next;
27 |
28 | /**
29 | * 上一个节点
30 | */
31 | protected TimerTask pre;
32 |
33 | /**
34 | * 描述
35 | */
36 | public String desc;
37 |
38 | public TimerTask(long delayMs, Runnable task) {
39 | this.delayMs = System.currentTimeMillis() + delayMs;
40 | this.task = task;
41 | this.timerTaskList = null;
42 | this.next = null;
43 | this.pre = null;
44 | }
45 |
46 | public Runnable getTask() {
47 | return task;
48 | }
49 |
50 | public long getDelayMs() {
51 | return delayMs;
52 | }
53 |
54 | @Override
55 | public String toString() {
56 | return desc;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/com/utils/TimerTaskList.java:
--------------------------------------------------------------------------------
1 | package com.utils;
2 |
3 | import java.util.concurrent.Delayed;
4 | import java.util.concurrent.TimeUnit;
5 | import java.util.concurrent.atomic.AtomicLong;
6 | import java.util.function.Consumer;
7 |
8 | /**
9 | * 时间槽
10 | */
11 | public class TimerTaskList implements Delayed {
12 |
13 | /**
14 | * 过期时间
15 | */
16 | private AtomicLong expiration = new AtomicLong(-1L);
17 |
18 | /**
19 | * 根节点
20 | */
21 | private TimerTask root = new TimerTask(-1L, null);
22 |
23 | {
24 | root.pre = root;
25 | root.next = root;
26 | }
27 |
28 | /**
29 | * 设置过期时间
30 | */
31 | public boolean setExpiration(long expire) {
32 | return expiration.getAndSet(expire) != expire;
33 | }
34 |
35 | /**
36 | * 获取过期时间
37 | */
38 | public long getExpiration() {
39 | return expiration.get();
40 | }
41 |
42 | /**
43 | * 新增任务
44 | */
45 | public void addTask(TimerTask timerTask) {
46 | synchronized (this) {
47 | if (timerTask.timerTaskList == null) {
48 | timerTask.timerTaskList = this;
49 | TimerTask tail = root.pre;
50 | timerTask.next = root;
51 | timerTask.pre = tail;
52 | tail.next = timerTask;
53 | root.pre = timerTask;
54 | }
55 | }
56 | }
57 |
58 | /**
59 | * 移除任务
60 | */
61 | public void removeTask(TimerTask timerTask) {
62 | synchronized (this) {
63 | if (timerTask.timerTaskList.equals(this)) {
64 | timerTask.next.pre = timerTask.pre;
65 | timerTask.pre.next = timerTask.next;
66 | timerTask.timerTaskList = null;
67 | timerTask.next = null;
68 | timerTask.pre = null;
69 | }
70 | }
71 | }
72 |
73 | /**
74 | * 重新分配
75 | */
76 | public synchronized void flush(Consumer flush) {
77 | TimerTask timerTask = root.next;
78 | while (!timerTask.equals(root)) {
79 | this.removeTask(timerTask);
80 | flush.accept(timerTask);
81 | timerTask = root.next;
82 | }
83 | expiration.set(-1L);
84 | }
85 |
86 | @Override
87 | public long getDelay(TimeUnit unit) {
88 | return Math.max(0, unit.convert(expiration.get() - System.currentTimeMillis(), TimeUnit.MILLISECONDS));
89 | }
90 |
91 | @Override
92 | public int compareTo(Delayed o) {
93 | if (o instanceof TimerTaskList) {
94 | return Long.compare(expiration.get(), ((TimerTaskList) o).expiration.get());
95 | }
96 | return 0;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/timer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Xun-Zhou/timing_wheel/a9a3223e9f8d5d6f3ca5e067251159e98b59fd3c/timer.png
--------------------------------------------------------------------------------
/timing_wheel.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/timing_wheel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Xun-Zhou/timing_wheel/a9a3223e9f8d5d6f3ca5e067251159e98b59fd3c/timing_wheel.png
--------------------------------------------------------------------------------