HashedWheelTimer
Netty
的HashedWheelTimer
类基于时间轮算法实现
主要成员变量
private final Worker worker = new Worker(); // 任务类,继承Runnable接口
private final Thread workerThread; // 工作线程,用来执行worker任务
private volatile int workerState; // 任务状态,cas自旋和volatile保证原子操作,0-初始化中, 1-开启, 2-关闭
private final long tickDuration; // wheel上每个时间片执行的时间间隔
private final HashedWheelBucket[] wheel; // 时间轮,HashedWheelBucket内部由双向队列实现
private final int mask;
private final CountDownLatch startTimeInitialized = new CountDownLatch(1); // 用于等待任务启动完成
private final Queue<HashedWheelTimeout> timeouts = PlatformDependent.newMpscQueue(); // 添加的任务
private final Queue<HashedWheelTimeout> cancelledTimeouts = PlatformDependent.newMpscQueue(); // 取消的任务
private final AtomicLong pendingTimeouts = new AtomicLong(0); // 等待的任务数
private final long maxPendingTimeouts; // 最大等待任务数
private volatile long startTime; // 延迟任务的开始时间
构造函数
public HashedWheelTimer(
ThreadFactory threadFactory,
long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection,
long maxPendingTimeouts) {
// 线程工厂,用来创建worker线程
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
// 每个tick运行的时间间隔单位
if (unit == null) {
throw new NullPointerException("unit");
}
// 每个tick运行的时间间隔
if (tickDuration <= 0) {
throw new IllegalArgumentException("tickDuration must be greater than 0: " + tickDuration);
}
// wheel数组即时间轮的大小
if (ticksPerWheel <= 0) {
throw new IllegalArgumentException("ticksPerWheel must be greater than 0: " + ticksPerWheel);
}
// 将wheel数组大小标准化为2的n次方,同时初始化wheel数组
wheel = createWheel(ticksPerWheel);
// 掩码
mask = wheel.length - 1;
// Convert tickDuration to nanos.
this.tickDuration = unit.toNanos(tickDuration);
// 防止溢出
if (this.tickDuration >= Long.MAX_VALUE / wheel.length) {
throw new IllegalArgumentException(String.format(
"tickDuration: %d (expected: 0 < tickDuration in nanos < %d",
tickDuration, Long.MAX_VALUE / wheel.length));
}
// 创建工作线程
workerThread = threadFactory.newThread(worker);
// 是否泄漏监控
leak = leakDetection || !workerThread.isDaemon() ? leakDetector.track(this) : null;
// 最大等待任务数
this.maxPendingTimeouts = maxPendingTimeouts;
// 警告,限制HashedWheelTimer实例数为INSTANCE_COUNT_LIMIT个即64个,预防过多的线程影响性能
if (INSTANCE_COUNTER.incrementAndGet() > INSTANCE_COUNT_LIMIT &&
WARNED_TOO_MANY_INSTANCES.compareAndSet(false, true)) {
reportTooManyInstances();
}
}
创建时间轮函数createWheel()
private static HashedWheelBucket[] createWheel(int ticksPerWheel) {
if (ticksPerWheel <= 0) {
throw new IllegalArgumentException(
"ticksPerWheel must be greater than 0: " + ticksPerWheel);
}
if (ticksPerWheel > 1073741824) {
throw new IllegalArgumentException(
"ticksPerWheel may not be greater than 2^30: " + ticksPerWheel);
}
// 严格控制时间轮的大小为2的n次方
ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel);
// 初始化时间轮数组并赋值
HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel];
for (int i = 0; i < wheel.length; i ++) {
wheel[i] = new HashedWheelBucket();
}
return wheel;
}
// 大于指定值的最小2的n次方
private static int normalizeTicksPerWheel(int ticksPerWheel) {
int normalizedTicksPerWheel = 1;
while (normalizedTicksPerWheel < ticksPerWheel) {
normalizedTicksPerWheel <<= 1;
}
return normalizedTicksPerWheel;
}
添加任务函数newTimeout()
添加任务时,如果worker线程没启动则启动,同时向timeouts队列中生产timeout任务
public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
if (task == null) {
throw new NullPointerException("task");
}
if (unit == null) {
throw new NullPointerException("unit");
}
// 等待任务数+1
long pendingTimeoutsCount = pendingTimeouts.incrementAndGet();
// 超过最大等待任务数,拒绝加入
if (maxPendingTimeouts > 0 && pendingTimeoutsCount > maxPendingTimeouts) {
pendingTimeouts.decrementAndGet();
throw new RejectedExecutionException("Number of pending timeouts ("
+ pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending "
+ "timeouts (" + maxPendingTimeouts + ")");
}
// 开始任务
start();
// 延迟执行的时间
long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;
if (delay > 0 && deadline < 0) {
deadline = Long.MAX_VALUE;
}
// 添加到timeouts队列,这是一个MPSC队列,MPSC队列是多生产者单消费者无锁的并发队列,worker线程会对队列进行消费
HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);
timeouts.add(timeout);
return timeout;
}
这里看下start()函数如何保证启动一次
public void start() {
// 这里使用CAS算法确保原子操作,因此多个线程并发执行只有一次会执行成功
switch (WORKER_STATE_UPDATER.get(this)) {
case WORKER_STATE_INIT:
if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) {
workerThread.start();
}
break;
case WORKER_STATE_STARTED:
break;
case WORKER_STATE_SHUTDOWN:
throw new IllegalStateException("cannot be started once stopped");
default:
throw new Error("Invalid WorkerState");
}
// 等待worker线程初始化一些启动工作
while (startTime == 0) {
try {
startTimeInitialized.await();
} catch (InterruptedException ignore) {
// Ignore - it will be ready very soon.
}
}
}
核心逻辑在Worker内部类中
private final Set<Timeout> unprocessedTimeouts = new HashSet<Timeout>();
private long tick; // tick计数
run方法
核心逻辑
public void run() {
// 初始化startTime变量
startTime = System.nanoTime();
if (startTime == 0) {
// 初始化失败时的值
startTime = 1;
}
// 通知其他线程继续执行
startTimeInitialized.countDown();
do {
// 等待直到当前tick时间点到
final long deadline = waitForNextTick();
// 当前tick时间点到了
if (deadline > 0) {
// 对应时间轮中的索引
int idx = (int) (tick & mask);
// 处理取消的任务
processCancelledTasks();
// 当前tick需要处理的bucket
HashedWheelBucket bucket =
wheel[idx];
// 将timeouts队列移到bucket中
transferTimeoutsToBuckets();
// 处理掉当前bucket时间到或过期的任务
bucket.expireTimeouts(deadline);
// tick数+1,继续等待执行下一个tick时间点
tick++;
}
} while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED);
// Fill the unprocessedTimeouts so we can return them from stop() method.
for (HashedWheelBucket bucket: wheel) {
bucket.clearTimeouts(unprocessedTimeouts);
}
for (;;) {
HashedWheelTimeout timeout = timeouts.poll();
if (timeout == null) {
break;
}
if (!timeout.isCancelled()) {
unprocessedTimeouts.add(timeout);
}
}
processCancelledTasks();
}
waitForNextTick()
等待下一个tick时间点到来
private long waitForNextTick() {
// 当前tick需要等待时间
long deadline = tickDuration * (tick + 1);
for (;;) {
// 当前时间与开始时间的时间间隔
final long currentTime = System.nanoTime() - startTime;
// 剩余需要等待的时间
long sleepTimeMs = (deadline - currentTime + 999999) / 1000000;
// 当前时间已经超过当前tick执行所需等待的时间了,则返回,否则休眠后再判断
if (sleepTimeMs <= 0) {
if (currentTime == Long.MIN_VALUE) {
return -Long.MAX_VALUE;
} else {
return currentTime;
}
}
// windows系统bug
if (PlatformDependent.isWindows()) {
sleepTimeMs = sleepTimeMs / 10 * 10;
}
// 休眠
try {
Thread.sleep(sleepTimeMs);
} catch (InterruptedException ignored) {
// HashedWheelTimer shutdown导致的线程中断
if (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_SHUTDOWN) {
return Long.MIN_VALUE;
}
}
}
}
transferTimeoutsToBuckets()
每次提取最多10000个任务,避免过多的时间消耗在这里影响定时任务,然后将任务放到对应的tick所在的wheel数组中
private void transferTimeoutsToBuckets() {
for (int i = 0; i < 100000; i++) {
HashedWheelTimeout timeout = timeouts.poll();
if (timeout == null) {
// all processed
break;
}
if (timeout.state() == HashedWheelTimeout.ST_CANCELLED) {
// Was cancelled in the meantime.
continue;
}
// 需要的tick数
long calculated = timeout.deadline / tickDuration;
// 执行该任务的时间轮数
timeout.remainingRounds = (calculated - tick) / wheel.length;
// 如果需要的tick数小于当前tick,说明时间点已经过了,直接在当前tick执行
final long ticks = Math.max(calculated, tick);
// 对应的wheel位置
int stopIndex = (int) (ticks & mask);
// 加入到双向队列
HashedWheelBucket bucket = wheel[stopIndex];
bucket.addTimeout(timeout);
}
}
HashedWheelBucket.expireTimeouts()
执行过期任务
public void expireTimeouts(long deadline) {
HashedWheelTimeout timeout = head;
// process all timeouts
while (timeout != null) {
HashedWheelTimeout next = timeout.next;
if (timeout.remainingRounds <= 0) {
next = remove(timeout);
// 移除并执行
if (timeout.deadline <= deadline) {
// 这里会执行该任务
timeout.expire();
} else {
// The timeout was placed into a wrong slot. This should never happen.
throw new IllegalStateException(String.format(
"timeout.deadline (%d) > deadline (%d)", timeout.deadline, deadline));
}
} else if (timeout.isCancelled()) {
next = remove(timeout);
} else {
timeout.remainingRounds --;
}
timeout = next;
}
}
processCancelledTasks()
移除取消的任务
private void processCancelledTasks() {
for (;;) {
HashedWheelTimeout timeout = cancelledTimeouts.poll();
if (timeout == null) {
// all processed
break;
}
try {
timeout.remove();
} catch (Throwable t) {
if (logger.isWarnEnabled()) {
logger.warn("An exception was thrown while process a cancellation task", t);
}
}
}
}
void remove() {
HashedWheelBucket bucket = this.bucket;
if (bucket != null) {
bucket.remove(this);
} else {
timer.pendingTimeouts.decrementAndGet();
}
}