- 我们先来分析NioEventLoop执行普通任务
runAllTasks(timeout)
方法。内部第1行fetchFromScheduledTaskQueue()方法首先尝试从调度队列中获取已经到期需要执行的定时任务,并加入普通任务队列。
- 如果此时普通任务队列已满,则重新置入调度队列,等待下一次轮训处理。因此,对于调度任务,执行时间未必一定是指定的时间。
- 每次从调度队列中获取时只会判断第一个任务是否到达执行时间。如果多个任务都到了执行时间,后续到达时间的任务将在下次loop中获取。
private boolean fetchFromScheduledTaskQueue() {
if (scheduledTaskQueue == null || scheduledTaskQueue.isEmpty()) {
return true;
}
long nanoTime = AbstractScheduledEventExecutor.nanoTime();
for (;;) {
Runnable scheduledTask = pollScheduledTask(nanoTime);
if (scheduledTask == null) {
return true;
}
if (!taskQueue.offer(scheduledTask)) {
scheduledTaskQueue.add((ScheduledFutureTask<?>) scheduledTask);
return false;
}
}
}
- 接下来第2行即从普通队列中获取任务。如果获取到任务,则在safeExecute(task)方法中安全执行,之所以称之为安全执行是因为内部任务运行通过
try{}catch{}
包裹,防止某个任务执行时抛出异常,影响后续任务的执行。执行完后继续获取下一个普通任务继续执行。直到普通队列中的任务执行完毕或超过了入参timeout时则返回。需要注意的时,这里探测是否超出timeout时间是每64个任务检查一次。所以,如果入参timeout=0,则会至少执行64个任务。
protected boolean runAllTasks(long timeoutNanos) {
fetchFromScheduledTaskQueue(); // 尝试从调度队列中获取到期的任务,加入普通队列。
Runnable task = pollTask();
if (task == null) {
afterRunningAllTasks();
return false;
}
final long deadline = timeoutNanos > 0 ? ScheduledFutureTask.nanoTime() + timeoutNanos : 0;
long runTasks = 0;
long lastExecutionTime;
for (;;) {
safeExecute(task) {
try {
task.run();
} catch (Throwable t) {
logger.warn("A task raised an exception. Task: {}", task, t);
}
}
runTasks ++;
if ((runTasks & 0x3F) == 0) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
if (lastExecutionTime >= deadline) {
break;
}
}
task = pollTask();
if (task == null) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
break;
}
}
afterRunningAllTasks();
this.lastExecutionTime = lastExecutionTime;
return true;
}
- 每次执行完任务后,都会执行afterRunningAllTasks()方法。
protected void afterRunningAllTasks() {
runAllTasksFrom(tailTasks);
}
protected final boolean runAllTasksFrom(Queue<Runnable> taskQueue) {
Runnable task = pollTaskFrom(taskQueue);
if (task == null) {
return false;
}
for (;;) {
safeExecute(task);
task = pollTaskFrom(taskQueue);
if (task == null) {
return true;
}
}
}