ScheduledFutureTask
//Netty当中的异步任务,把逻辑封装到此类异步完成后获取结果
final class ScheduledFutureTask<V> extends PromiseTask<V> implements ScheduledFuture<V>, PriorityQueueNode {
//任务ID 自增,唯一
private static final AtomicLong nextTaskId = new AtomicLong();
//启动纳秒
private static final long START_TIME = System.nanoTime();
//当前纳秒减去启动纳秒,相当于一个自增的时间差值
static long nanoTime() {
return System.nanoTime() - START_TIME;
}
//给定延迟时间,计算到期时间
static long deadlineNanos(long delay) {
long deadlineNanos = nanoTime() + delay;
// Guard against overflow 防止溢出
return deadlineNanos < 0 ? Long.MAX_VALUE : deadlineNanos;
}
//成员变量-自增ID
private final long id = nextTaskId.getAndIncrement();
//到期时间
private long deadlineNanos;
private final long periodNanos;
//任务在队列中的位置
private int queueIndex = INDEX_NOT_IN_QUEUE;
ScheduledFutureTask(
AbstractScheduledEventExecutor executor,
Callable<V> callable, long nanoTime, long period) {
//调用父类构造参数
//父类源码分析在https://blog.csdn.net/nimasike/article/details/102024585
super(executor, callable);
if (period == 0) {
throw new IllegalArgumentException("period: 0 (expected: != 0)");
}
deadlineNanos = nanoTime;
periodNanos = period;
}
//deadlineNanos = 创建此类时的(当前时间) -差值(固定值) + delay 目的让数变小一点
//nanoTime()= 系统调用当前时间 - 差值(固定值)
//deadlineNanos - nanoTime() = 创建此类时的(当前时间) -差值(固定值) + delay - (系统调用当前时间 - 差值(固定值))
//创建此类时的(当前时间) + delay - 系统调用当前时间
//值随着时间的推移会小于0,则意味着到期了。
public long delayNanos() {
return Math.max(0, deadlineNanos() - nanoTime());
}
//计算到期时间,使用currentTimeNanos参数而不是系统时间,原理同上
public long delayNanos(long currentTimeNanos) {
return Math.max(0, deadlineNanos() - (currentTimeNanos - START_TIME));
}
//根据时间单元获取到期时间
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(delayNanos(), TimeUnit.NANOSECONDS);
}
//比较到期时间大小
@Override
public int compareTo(Delayed o) {
if (this == o) {
return 0;
}
ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o;
long d = deadlineNanos() - that.deadlineNanos();
if (d < 0) {
return -1;
} else if (d > 0) {
return 1;
} else if (id < that.id) {
return -1;
} else if (id == that.id) {
throw new Error();
} else {
return 1;
}
}
@Override
public void run() {
//必须要在executor的线程中执行
assert executor().inEventLoop();
try {
//如果为0执行一次获取结果设置成功状态
if (periodNanos == 0) {
if (setUncancellableInternal()) {
V result = task.call();
setSuccessInternal(result);
}
} else {
// check if is done as it may was cancelled
//检查是否取消
if (!isCancelled()) {
//调用具体任务逻辑
task.call();
//判断executor是否结束
if (!executor().isShutdown()) {
long p = periodNanos;
//以固定速率重复, 打个比方,说白了就是不管任务具体执行多久,任务总是在1秒,5秒,10秒,15秒时刻运行
//也就是将在 initialDelay 后开始执行,然后在 initialDelay+p 后执行,接着在 initialDelay + 2 * p 后执行,依此类推。
//有一个特殊情况 如果此任务的任何一个执行要花费比其周期更长的时间,则将推迟后续执行,但不会同时执行。
//比如任务间隔p是5秒,但是任务执行了10秒,那么不会在第5秒的时刻同时执行这个任务,会在10秒结束后立即执行这个任务。
if (p > 0) {
deadlineNanos += p;
} else {
//以固定速率延迟
//它是在上一次任务完成后,延迟多久再次运行,比如上次任务1秒,5秒执行。
//假设在5秒执行的任务运行了25秒,此时到达30秒时刻,那么任务再35秒才会再次运行
deadlineNanos = nanoTime() - p;
}
//检查是否取消
if (!isCancelled()) {
// scheduledTaskQueue can never be null as we lazy init it before submit the task!
Queue<ScheduledFutureTask<?>> scheduledTaskQueue =
((AbstractScheduledEventExecutor) executor()).scheduledTaskQueue;
assert scheduledTaskQueue != null;
//把当前任务加入任务队列
scheduledTaskQueue.add(this);
}
}
}
}
} catch (Throwable cause) {
//异常设置失败状态
setFailureInternal(cause);
}
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
boolean canceled = super.cancel(mayInterruptIfRunning);
if (canceled) {
((AbstractScheduledEventExecutor) executor()).removeScheduled(this);
}
return canceled;
}
boolean cancelWithoutRemove(boolean mayInterruptIfRunning) {
return super.cancel(mayInterruptIfRunning);
}
//获取在队列中的索引位置
@Override
public int priorityQueueIndex(DefaultPriorityQueue<?> queue) {
return queueIndex;
}
//设置在队列中的索引位置
@Override
public void priorityQueueIndex(DefaultPriorityQueue<?> queue, int i) {
queueIndex = i;
}
}
用java的类测试scheduleAtFixedRate方法,当间隔时间小于任务的执行时间,任务会每隔5秒运行。
比如0秒时刻,5秒时刻,10秒时刻。
ScheduledThreadPoolExecutor sExecutor = new ScheduledThreadPoolExecutor(30);
sExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + "task is run 10 second" + new Date());
try {
Thread.sleep(3000);
System.out.println(Thread.currentThread() + "task is run 10 second end" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 0, 5, TimeUnit.SECONDS);
Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:17:19 CST 2019 任务开始时间
Thread[pool-1-thread-1,5,main]task is run 10 second endThu Oct 10 15:17:22 CST 2019 任务结束时间,执行了3秒
Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:17:24 CST 2019 第二个任务开始时间,和第一个差5秒没问题。
如果任务间隔小于任务的实际执行时间,那么已实际执行时间为准,不会并发启动第二个任务。
ScheduledThreadPoolExecutor sExecutor = new ScheduledThreadPoolExecutor(30);
sExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + "task is run 10 second" + new Date());
try {
Thread.sleep(10000);
System.out.println(Thread.currentThread() + "task is run 10 second end" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 0, 5, TimeUnit.SECONDS);
}
Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:16:06 CST 2019 任务开始时间
Thread[pool-1-thread-1,5,main]task is run 10 second endThu Oct 10 15:16:16 CST 2019 任务结束时间,执行了10秒。
Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:16:16 CST 2019 第二个任务开始时间,与第一个间隔了10秒,而不是在第5秒时启动第二个线程运行。
scheduleWithFixedDelay是再任务结束后计算间隔时间,不管前一个任务允许多久,比如允许了10秒,那么它是在第一个任务执行后5秒执行第二个任务。
ScheduledThreadPoolExecutor sExecutor = new ScheduledThreadPoolExecutor(30);
sExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + "task is run 10 second" + new Date());
try {
Thread.sleep(3000);
System.out.println(Thread.currentThread() + "task is run 10 second end" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 0, 5, TimeUnit.SECONDS);
Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:19:11 CST 2019 第一个任务执行时间
Thread[pool-1-thread-1,5,main]task is run 10 second endThu Oct 10 15:19:14 CST 2019 第一个任务执行结束时间,允许了3秒
Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:19:19 CST 2019 第二个任务允许时间实在结束时间上+5秒。