java hashed wheel timer netty_Netty HashedWheelTimer 时间轮

/*

* Copyright 2012 The Netty Project

*

* The Netty Project licenses this file to you under the Apache License,

* version 2.0 (the "License"); you may not use this file except in compliance

* with the License. You may obtain a copy of the License at:

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

* License for the specific language governing permissions and limitations

* under the License.

*/

package io.netty.util;

import io.netty.util.internal.PlatformDependent;

import io.netty.util.internal.logging.InternalLogger;

import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.Collections;

import java.util.HashSet;

import java.util.Queue;

import java.util.Set;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.Executors;

import java.util.concurrent.RejectedExecutionException;

import java.util.concurrent.ThreadFactory;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.atomic.AtomicBoolean;

import java.util.concurrent.atomic.AtomicInteger;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

import java.util.concurrent.atomic.AtomicLong;

import static io.netty.util.internal.StringUtil.simpleClassName;

/**

* A {@link Timer} optimized for approximated I/O timeout scheduling.

*

*

Tick Duration

*

* As described with 'approximated', this timer does not execute the scheduled

* {@link TimerTask} on time. {@link HashedWheelTimer}, on every tick, will

* check if there are any {@link TimerTask}s behind the schedule and execute

* them.

*

* You can increase or decrease the accuracy of the execution timing by

* specifying smaller or larger tick duration in the constructor. In most

* network applications, I/O timeout does not need to be accurate. Therefore,

* the default tick duration is 100 milliseconds and you will not need to try

* different configurations in most cases.

*

*

Ticks per Wheel (Wheel Size)

*

* {@link HashedWheelTimer} maintains a data structure called 'wheel'.

* To put simply, a wheel is a hash table of {@link TimerTask}s whose hash

* function is 'dead line of the task'. The default number of ticks per wheel

* (i.e. the size of the wheel) is 512. You could specify a larger value

* if you are going to schedule a lot of timeouts.

*

*

Do not create many instances.

*

* {@link HashedWheelTimer} creates a new thread whenever it is instantiated and

* started. Therefore, you should make sure to create only one instance and

* share it across your application. One of the common mistakes, that makes

* your application unresponsive, is to create a new instance for every connection.

*

*

Implementation Details

*

* {@link HashedWheelTimer} is based on

* George Varghese and

* Tony Lauck's paper,

* 'Hashed

* and Hierarchical Timing Wheels: data structures to efficiently implement a

* timer facility'. More comprehensive slides are located

* here.

*/

public class HashedWheelTimer implements Timer {

static final InternalLogger logger = InternalLoggerFactory.getInstance(HashedWheelTimer.class);

// 实例数 静态变量共享

private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger();

private static final AtomicBoolean WARNED_TOO_MANY_INSTANCES = new AtomicBoolean();

// 实例数最多 64

private static final int INSTANCE_COUNT_LIMIT = 64;

// netty 资源泄露探测

private static final ResourceLeakDetector leakDetector = ResourceLeakDetectorFactory.instance()

.newResourceLeakDetector(HashedWheelTimer.class, 1);

// 工作线程状态 原子更新

private static final AtomicIntegerFieldUpdater WORKER_STATE_UPDATER =

AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimer.class, "workerState");

private final ResourceLeakTracker leak;

// 工作线程 Runnable 实现

private final Worker worker = new Worker();

// 工作线程

private final Thread workerThread;

// 工作线程状态

public static final int WORKER_STATE_INIT = 0;

public static final int WORKER_STATE_STARTED = 1;

public static final int WORKER_STATE_SHUTDOWN = 2;

// 工作线程状态 volatile 变量保存内存可见性,配合 AtomicIntegerFieldUpdater 原子字段更新类进行 CAS 操作使用

@SuppressWarnings({"unused", "FieldMayBeFinal"})

private volatile int workerState; // 0 - init, 1 - started, 2 - shut down

// 时钟走一格的时间间隔

private final long tickDuration;

// 槽位 时钟总格数

private final HashedWheelBucket[] wheel;

private final int mask;

// 同步工具 用于同步启动的操作

private final CountDownLatch startTimeInitialized = new CountDownLatch(1);

// 执行任务 mpsc 队列

private final Queue timeouts = PlatformDependent.newMpscQueue();

// 已经取消 mpsc队列

private final Queue cancelledTimeouts = PlatformDependent.newMpscQueue();

// 目前执行任务数

private final AtomicLong pendingTimeouts = new AtomicLong(0);

// 最大执行任务数

private final long maxPendingTimeouts;

// 开始时间 volatile 变量保存内存可见性

private volatile long startTime;

/**

* Creates a new timer with the default thread factory

* ({@link Executors#defaultThreadFactory()}), default tick duration, and

* default number of ticks per wheel.

*/

public HashedWheelTimer() {

this(Executors.defaultThreadFactory());

}

/**

* Creates a new timer with the default thread factory

* ({@link Executors#defaultThreadFactory()}) and default number of ticks

* per wheel.

*

* @param tickDuration the duration between tick

* @param unit the time unit of the {@code tickDuration}

*

* @throws NullPointerException if {@code unit} is {@code null}

* @throws IllegalArgumentException if {@code tickDuration} is <= 0

*/

public HashedWheelTimer(long tickDuration, TimeUnit unit) {

this(Executors.defaultThreadFactory(), tickDuration, unit);

}

/**

* Creates a new timer with the default thread factory

* ({@link Executors#defaultThreadFactory()}).

*

* @param tickDuration the duration between tick

* @param unit the time unit of the {@code tickDuration}

* @param ticksPerWheel the size of the wheel

*

* @throws NullPointerException if {@code unit} is {@code null}

* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0

*/

public HashedWheelTimer(long tickDuration, TimeUnit unit, int ticksPerWheel) {

this(Executors.defaultThreadFactory(), tickDuration, unit, ticksPerWheel);

}

/**

* Creates a new timer with the default tick duration and default number of

* ticks per wheel.

*

* @param threadFactory a {@link ThreadFactory} that creates a

* background {@link Thread} which is dedicated to

* {@link TimerTask} execution.

*

* @throws NullPointerException if {@code threadFactory} is {@code null}

*/

public HashedWheelTimer(ThreadFactory threadFactory) {

this(threadFactory, 100, TimeUnit.MILLISECONDS);

}

/**

* Creates a new timer with the default number of ticks per wheel.

*

* @param threadFactory a {@link ThreadFactory} that creates a

* background {@link Thread} which is dedicated to

* {@link TimerTask} execution.

* @param tickDuration the duration between tick

* @param unit the time unit of the {@code tickDuration}

*

* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}

* @throws IllegalArgumentException if {@code tickDuration} is <= 0

*/

public HashedWheelTimer(

ThreadFactory threadFactory, long tickDuration, TimeUnit unit) {

this(threadFactory, tickDuration, unit, 512);

}

/**

* Creates a new timer.

*

* @param threadFactory a {@link ThreadFactory} that creates a

* background {@link Thread} which is dedicated to

* {@link TimerTask} execution.

* @param tickDuration the duration between tick

* @param unit the time unit of the {@code tickDuration}

* @param ticksPerWheel the size of the wheel

*

* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}

* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0

*/

public HashedWheelTimer(

ThreadFactory threadFactory,

long tickDuration, TimeUnit unit, int ticksPerWheel) {

this(threadFactory, tickDuration, unit, ticksPerWheel, true);

}

/**

* Creates a new timer.

*

* @param threadFactory a {@link ThreadFactory} that creates a

* background {@link Thread} which is dedicated to

* {@link TimerTask} execution.

* @param tickDuration the duration between tick

* @param unit the time unit of the {@code tickDuration}

* @param ticksPerWheel the size of the wheel

* @param leakDetection {@code true} if leak detection should be enabled always,

* if false it will only be enabled if the worker thread is not

* a daemon thread.

*

* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}

* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0

*/

public HashedWheelTimer(

ThreadFactory threadFactory,

long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection) {

this(threadFactory, tickDuration, unit, ticksPerWheel, leakDetection, -1);

}

/**

* Creates a new timer.

*

* @param threadFactory a {@link ThreadFactory} that creates a

* background {@link Thread} which is dedicated to

* {@link TimerTask} execution.

* @param tickDuration the duration between tick

* @param unit the time unit of the {@code tickDuration}

* @param ticksPerWheel the size of the wheel

* @param leakDetection {@code true} if leak detection should be enabled always,

* if false it will only be enabled if the worker thread is not

* a daemon thread.

* @param maxPendingTimeouts The maximum number of pending timeouts after which call to

* {@code newTimeout} will result in

* {@link java.util.concurrent.RejectedExecutionException}

* being thrown. No maximum pending timeouts limit is assumed if

* this value is 0 or negative.

*

* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}

* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0

*/

public HashedWheelTimer(

ThreadFactory threadFactory,

long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection,

long maxPendingTimeouts) {

if (threadFactory == null) {

throw new NullPointerException("threadFactory");

}

if (unit == null) {

throw new NullPointerException("unit");

}

if (tickDuration <= 0) {

throw new IllegalArgumentException("tickDuration must be greater than 0: " + tickDuration);

}

if (ticksPerWheel <= 0) {

throw new IllegalArgumentException("ticksPerWheel must be greater than 0: " + ticksPerWheel);

}

// Normalize ticksPerWheel to power of two and initialize the wheel.

// 创建时钟格子 也就是槽位

wheel = createWheel(ticksPerWheel);

// 实际需要取模的数字 因为下表从 0 开始

mask = wheel.length - 1;

// 每个时间格子间隔时间 转换成 纳秒

this.tickDuration = unit.toNanos(tickDuration);

// 防止溢出

if (this.tickDuration >= Long.MAX_VALUE / wheel.length) {

// 每一格的时间大于等于 long 最大值/格子数时候

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;

// CAS 增加实例数 大于 限定值 并且 CAS 更新想要更多实例字段 成功

if (INSTANCE_COUNTER.incrementAndGet() > INSTANCE_COUNT_LIMIT &&

WARNED_TOO_MANY_INSTANCES.compareAndSet(false, true)) {

// 输出实例过多的错误日志

reportTooManyInstances();

}

}

@Override

protected void finalize() throws Throwable {

try {

super.finalize();

} finally {

// This object is going to be GCed and it is assumed the ship has sailed to do a proper shutdown. If

// we have not yet shutdown then we want to make sure we decrement the active instance count.

if (WORKER_STATE_UPDATER.getAndSet(this, WORKER_STATE_SHUTDOWN) != WORKER_STATE_SHUTDOWN) {

INSTANCE_COUNTER.decrementAndGet();

}

}

}

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次方,因为获取槽位或者时间格子位置是使用位运算,位运算 & 比取模 mod 效率高

ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel);

HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel];

for (int i = 0; i < wheel.length; i++) {

wheel[i] = new HashedWheelBucket();

}

return wheel;

}

private static int normalizeTicksPerWheel(int ticksPerWheel) {

int normalizedTicksPerWheel = 1;

// 直到 2 的 n 次方大于格子数(参数)

while (normalizedTicksPerWheel < ticksPerWheel) {

normalizedTicksPerWheel <<= 1;

}

return normalizedTicksPerWheel;

}

/**

* Starts the background thread explicitly. The background thread will

* start automatically on demand even if you did not call this method.

*

* @throws IllegalStateException if this timer has been

* {@linkplain #stop() stopped} already

*/

public void start() {

// 获取当前工作线程状态

switch (WORKER_STATE_UPDATER.get(this)) {

case WORKER_STATE_INIT:

// CAS 修改状态

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");

}

// Wait until the startTime is initialized by the worker.

// 一直是 0 就是为初始化

while (startTime == 0) {

try {

// CountDownLatch 等待

startTimeInitialized.await();

} catch (InterruptedException ignore) {

// Ignore - it will be ready very soon.

}

}

}

@Override

public Set stop() {

if (Thread.currentThread() == workerThread) {

// 如果当前线程为工作线程,不允许操作

throw new IllegalStateException(

HashedWheelTimer.class.getSimpleName() +

".stop() cannot be called from " +

TimerTask.class.getSimpleName());

}

// CAS 设置成为关闭状态 不成功时候

if (!WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_STARTED, WORKER_STATE_SHUTDOWN)) {

// workerState can be 0 or 2 at this moment - let it always be 2.

if (WORKER_STATE_UPDATER.getAndSet(this, WORKER_STATE_SHUTDOWN) != WORKER_STATE_SHUTDOWN) {

INSTANCE_COUNTER.decrementAndGet();

if (leak != null) {

boolean closed = leak.close(this);

assert closed;

}

}

// 返回空集合

return Collections.emptySet();

}

try {

boolean interrupted = false;

while (workerThread.isAlive()) {

// 中断 work 线程

workerThread.interrupt();

try {

workerThread.join(100);

} catch (InterruptedException ignored) {

interrupted = true;

}

}

if (interrupted) {

// 如果 work 线程出现中断异常,当前线程进行中断操作

Thread.currentThread().interrupt();

}

} finally {

// 减少实例数

INSTANCE_COUNTER.decrementAndGet();

if (leak != null) {

boolean closed = leak.close(this);

assert closed;

}

}

// 返回 worker 当前未完成任务

return worker.unprocessedTimeouts();

}

@Override

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();

// Add the timeout to the timeout queue which will be processed on the next tick.

// During processing all the queued HashedWheelTimeouts will be added to the correct HashedWheelBucket.

// 计算截止时间

long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;

// Guard against overflow.

if (delay > 0 && deadline < 0) {

deadline = Long.MAX_VALUE;

}

// 新增执行任务

HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);

timeouts.add(timeout);

return timeout;

}

/**

* Returns the number of pending timeouts of this {@link Timer}.

*/

public long pendingTimeouts() {

return pendingTimeouts.get();

}

private static void reportTooManyInstances() {

if (logger.isErrorEnabled()) {

String resourceType = simpleClassName(HashedWheelTimer.class);

logger.error("You are creating too many " + resourceType + " instances. " +

resourceType + " is a shared resource that must be reused across the JVM," +

"so that only a few instances are created.");

}

}

private final class Worker implements Runnable {

// 未完成

private final Set unprocessedTimeouts = new HashSet();

// 转过格子的次数

private long tick;

@Override

public void run() {

// Initialize the startTime.

startTime = System.nanoTime();

if (startTime == 0) {

// 这里使用0作为未初始化的值,所以确保不是0就是已经初始化了

// We use 0 as an indicator for the uninitialized value here, so make sure it's not 0 when initialized.

startTime = 1;

}

// Notify the other threads waiting for the initialization at start().

startTimeInitialized.countDown();

do {

// waitForNextTick 方法主要是计算下次 tick 的时间, 然后 sleep 到下次 tick

final long deadline = waitForNextTick();

// 大于 0 说明已经转完这个格子,到了下一次tick

if (deadline > 0) {

// 下面操作是对这次 tick 的处理

int idx = (int) (tick & mask);

processCancelledTasks();

HashedWheelBucket bucket =

wheel[idx];

transferTimeoutsToBuckets();

bucket.expireTimeouts(deadline);

// 转动格子数 + 1

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();

}

private void transferTimeoutsToBuckets() {

// 所有可以执行的任务全部拿出来放进对应的槽(格子)

// transfer only max. 100000 timeouts per tick to prevent a thread to stale the workerThread when it just

// adds new timeouts in a loop.

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;

}

// 计算一下这个任务需要转多少个格子

long calculated = timeout.deadline / tickDuration;

// 需要转动格子-当前已经转格子数 / 一轮的长度 = 还需要多少圈

timeout.remainingRounds = (calculated - tick) / wheel.length;

// 获取最大的转动数,不执行过去的任务

final long ticks = Math.max(calculated, tick); // Ensure we don't schedule for past.

// 位运算取索引位

int stopIndex = (int) (ticks & mask);

// 放入对应格子(槽)

HashedWheelBucket bucket = wheel[stopIndex];

bucket.addTimeout(timeout);

}

}

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);

}

}

}

}

/**

* calculate goal nanoTime from startTime and current tick number,

* then wait until that goal has been reached.

*

* @return Long.MIN_VALUE if received a shutdown request,

* current time otherwise (with Long.MIN_VALUE changed by +1)

*/

private long waitForNextTick() {

// 当前格子的截止时间

long deadline = tickDuration * (tick + 1);

for (; ; ) {

// 当前时间和开始时间的间隔时间

final long currentTime = System.nanoTime() - startTime;

// 睡眠时间计算 之所以加999999后再除10000000, 是为了保证足够的sleep时间

// 解决的问题,我举个例子,因为这里是ms,假设应该睡2.02ms,但是实际上只会睡2ms(时间精度问题,只能精确到ms),加999999只会,会变成3ms。

// 实际就是宁可多睡不少睡

long sleepTimeMs = (deadline - currentTime + 999999) / 1000000;

// long sleepTimeMs = (deadline + startTime - System.nanoTime() + 999999) / 1000000;

if (sleepTimeMs <= 0) {

if (currentTime == Long.MIN_VALUE) {

return -Long.MAX_VALUE;

} else {

return currentTime;

}

}

// Check if we run on windows, as if thats the case we will need

// to round the sleepTime as workaround for a bug that only affect

// the JVM if it runs on windows.

//

// See https://github.com/netty/netty/issues/356

if (PlatformDependent.isWindows()) {

// Windows 必须 10 倍数时间

sleepTimeMs = sleepTimeMs / 10 * 10;

}

try {

// 休眠相应时间

Thread.sleep(sleepTimeMs);

} catch (InterruptedException ignored) {

// 如果收到响应中断 获取相应的状态,如果是关闭状态,返回最小值

if (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_SHUTDOWN) {

return Long.MIN_VALUE;

}

}

}

}

public Set unprocessedTimeouts() {

return Collections.unmodifiableSet(unprocessedTimeouts);

}

}

private static final class HashedWheelTimeout implements Timeout {

// 任务状态

private static final int ST_INIT = 0;

private static final int ST_CANCELLED = 1;

private static final int ST_EXPIRED = 2;

// 任务状态原子更新 io.netty.util.HashedWheelTimer.HashedWheelTimeout.state

private static final AtomicIntegerFieldUpdater STATE_UPDATER =

AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimeout.class, "state");

// 当前所属时间轮引用

private final HashedWheelTimer timer;

// 任务内容

private final TimerTask task;

// 过期截止时间

private final long deadline;

@SuppressWarnings({"unused", "FieldMayBeFinal", "RedundantFieldInitialization"})

// volatile 变量保证可见性

private volatile int state = ST_INIT;

// remainingRounds will be calculated and set by Worker.transferTimeoutsToBuckets() before the

// HashedWheelTimeout will be added to the correct HashedWheelBucket.

long remainingRounds;

// This will be used to chain timeouts in HashedWheelTimerBucket via a double-linked-list.

// As only the workerThread will act on it there is no need for synchronization / volatile.

HashedWheelTimeout next;

HashedWheelTimeout prev;

// The bucket to which the timeout was added

HashedWheelBucket bucket;

HashedWheelTimeout(HashedWheelTimer timer, TimerTask task, long deadline) {

this.timer = timer;

this.task = task;

this.deadline = deadline;

}

@Override

public Timer timer() {

return timer;

}

@Override

public TimerTask task() {

return task;

}

@Override

public boolean cancel() {

// only update the state it will be removed from HashedWheelBucket on next tick.

// CAS 更新状态

if (!compareAndSetState(ST_INIT, ST_CANCELLED)) {

return false;

}

// If a task should be canceled we put this to another queue which will be processed on each tick.

// So this means that we will have a GC latency of max. 1 tick duration which is good enough. This way

// we can make again use of our MpscLinkedQueue and so minimize the locking / overhead as much as possible.

timer.cancelledTimeouts.add(this);

return true;

}

void remove() {

HashedWheelBucket bucket = this.bucket;

if (bucket != null) {

bucket.remove(this);

} else {

timer.pendingTimeouts.decrementAndGet();

}

}

public boolean compareAndSetState(int expected, int state) {

return STATE_UPDATER.compareAndSet(this, expected, state);

}

public int state() {

return state;

}

@Override

public boolean isCancelled() {

return state() == ST_CANCELLED;

}

@Override

public boolean isExpired() {

return state() == ST_EXPIRED;

}

public void expire() {

if (!compareAndSetState(ST_INIT, ST_EXPIRED)) {

// CAS 更新过期状态不成功直接返回

return;

}

try {

task.run(this);

} catch (Throwable t) {

if (logger.isWarnEnabled()) {

logger.warn("An exception was thrown by " + TimerTask.class.getSimpleName() + '.', t);

}

}

}

@Override

public String toString() {

final long currentTime = System.nanoTime();

long remaining = deadline - currentTime + timer.startTime;

StringBuilder buf = new StringBuilder(192)

.append(simpleClassName(this))

.append('(')

.append("deadline: ");

if (remaining > 0) {

buf.append(remaining)

.append(" ns later");

} else if (remaining < 0) {

buf.append(-remaining)

.append(" ns ago");

} else {

buf.append("now");

}

if (isCancelled()) {

buf.append(", cancelled");

}

return buf.append(", task: ")

.append(task())

.append(')')

.toString();

}

}

/**

* Bucket that stores HashedWheelTimeouts. These are stored in a linked-list like datastructure to allow easy

* removal of HashedWheelTimeouts in the middle. Also the HashedWheelTimeout act as nodes themself and so no

* extra object creation is needed.

*/

private static final class HashedWheelBucket {

// Used for the linked-list datastructure

private HashedWheelTimeout head;

private HashedWheelTimeout tail;

/**

* Add {@link HashedWheelTimeout} to this bucket.

*/

public void addTimeout(HashedWheelTimeout timeout) {

assert timeout.bucket == null;

timeout.bucket = this;

if (head == null) {

head = tail = timeout;

} else {

tail.next = timeout;

timeout.prev = tail;

tail = timeout;

}

}

/**

* Expire all {@link HashedWheelTimeout}s for the given {@code deadline}.

*/

public void expireTimeouts(long deadline) {

HashedWheelTimeout timeout = head;

// process all timeouts

while (timeout != null) {

HashedWheelTimeout next = timeout.next;

if (timeout.remainingRounds <= 0) {

// 剩余轮数<=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;

}

}

public HashedWheelTimeout remove(HashedWheelTimeout timeout) {

HashedWheelTimeout next = timeout.next;

// remove timeout that was either processed or cancelled by updating the linked-list

if (timeout.prev != null) {

timeout.prev.next = next;

}

if (timeout.next != null) {

timeout.next.prev = timeout.prev;

}

if (timeout == head) {

// if timeout is also the tail we need to adjust the entry too

if (timeout == tail) {

tail = null;

head = null;

} else {

head = next;

}

} else if (timeout == tail) {

// if the timeout is the tail modify the tail to be the prev node.

tail = timeout.prev;

}

// null out prev, next and bucket to allow for GC.

timeout.prev = null;

timeout.next = null;

timeout.bucket = null;

timeout.timer.pendingTimeouts.decrementAndGet();

return next;

}

/**

* Clear this bucket and return all not expired / cancelled {@link Timeout}s.

*/

public void clearTimeouts(Set set) {

for (; ; ) {

HashedWheelTimeout timeout = pollTimeout();

if (timeout == null) {

return;

}

if (timeout.isExpired() || timeout.isCancelled()) {

continue;

}

set.add(timeout);

}

}

private HashedWheelTimeout pollTimeout() {

HashedWheelTimeout head = this.head;

if (head == null) {

return null;

}

HashedWheelTimeout next = head.next;

if (next == null) {

tail = this.head = null;

} else {

this.head = next;

next.prev = null;

}

// null out prev and next to allow for GC.

head.next = null;

head.prev = null;

head.bucket = null;

return head;

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值