Nio对其他任务的处理
任务执行
run
首先回到NioEventLoop的run方法
if (ioRatio == 100) {
// 没有指定处理io事件占所有执行时间的比例
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
runAllTasks();
}
} else {
// 指定了处理io事件占所有执行时间的比例
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
runAllTasks
可以看到最终都会调用runAllTasks方法
protected boolean runAllTasks(long timeoutNanos) {
// 从定时任务队列中获取待执行任务
fetchFromScheduledTaskQueue();
// 从任务队头获取执行任务
Runnable task = pollTask();
// 如果队列中没有任务,那么执行运行完所有任务的回调
if (task == null) {
// 回调
afterRunningAllTasks();
return false;
}
// 计算执行deadline,当到达这个时间,将会跳出此次循环,不再执行任务
final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
long runTasks = 0;
long lastExecutionTime;
for (;;) {
// 调用任务的run方法
safeExecute(task);
runTasks ++;
// Check timeout every 64 tasks because nanoTime() is relatively expensive.
// XXX: Hard-coded value - will make it configurable if it is really a problem.
// 每64个任务判断一下是否到了deadline
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;
}
fetchFromSchedeuledTaskQueue
从定时任务队列中获取到期任务,然后添加到任务队列中
如果添加失败,将任务重新添加会定时任务队列
private boolean fetchFromScheduledTaskQueue() {
long nanoTime = AbstractScheduledEventExecutor.nanoTime();
// 从定时任务队列的头部获取当前已经到达deadline的定时任务
Runnable scheduledTask = pollScheduledTask(nanoTime);
while (scheduledTask != null) {
// 尝试将到达deadline的定时任务添加到一般任务队列的末尾
if (!taskQueue.offer(scheduledTask)) {
// No space left in the task queue add it back to the scheduledTaskQueue so we pick it up again.
// 如果添加失败,将该任务重新添加到定时任务队列中
scheduledTaskQueue().add((ScheduledFutureTask<?>) scheduledTask);
return false;
}
// 继续从定时任务队列的头部取出到了deadline的定时任务
scheduledTask = pollScheduledTask(nanoTime);
}
return true;
}
下面看下是如何从定时任务队列中取出任务的,可以看到代码比较简单,就是获取定时任务队里头部的任务,然后判断其deadline是否到期
protected final Runnable pollScheduledTask(long nanoTime) {
assert inEventLoop();
Queue<ScheduledFutureTask<?>> scheduledTaskQueue = this.scheduledTaskQueue;
ScheduledFutureTask<?> scheduledTask = scheduledTaskQueue == null ? null : scheduledTaskQueue.peek();
if (scheduledTask == null) {
return null;
}
if (scheduledTask.deadlineNanos() <= nanoTime) {
scheduledTaskQueue.remove();
return scheduledTask;
}
return null;
}
pollTask
接下来看下如何从任务队列中取出接下来要执行的任务
protected Runnable pollTask() {
assert inEventLoop();
return pollTaskFrom(taskQueue);
}
protected static Runnable pollTaskFrom(Queue<Runnable> taskQueue) {
// 从任务队列中跳过唤醒任务
for (;;) {
Runnable task = taskQueue.poll();
if (task == WAKEUP_TASK) {
continue;
}
return task;
}
}
afterRunningAllTasks
当执行完任务队列中的所有任务或者deadline到了的时候,会执行afterRunningAllTasks回调
afterRunningAllTasks由SingleThreadEventExecutor的子类来进行实现
下面看下子类SingleTheadEventLoop的实现,比较简单,就是从tailQueue这个任务队列中获取任务然后执行
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;
}
}
}
定时任务添加
首先Netty中用来代表定时任务的类ScheduledFutureTask
ScheduledFutureTask
重要属性
// 递增的任务编号
private static final AtomicLong nextTaskId = new AtomicLong();
// 开始时间
private static final long START_TIME = System.nanoTime();
// 当前任务的任务编号
private final long id = nextTaskId.getAndIncrement();
// deadline时间
private long deadlineNanos;
/* 0 - no repeat, >0 - repeat at fixed rate, <0 - repeat with fixed delay */
// 0 不需要重复
// >0 需要重复,并且按照任务的开始的时间保持一定间隔进行重复执行
// <0 需要重复,并且按照任务的结束时间保持一定间隔重复执行
private final long periodNanos;
构造方法
ScheduledFutureTask(
AbstractScheduledEventExecutor executor,
Callable<V> callable, long nanoTime, long period) {
super(executor, callable);
if (period == 0) {
throw new IllegalArgumentException("period: 0 (expected: != 0)");
}
// deadlineNanos是相对当前时间的相对时间
deadlineNanos = nanoTime;
periodNanos = period;
}
nanoTime
计算当前系统时间和开始时间的间隔
static long nanoTime() {
return System.nanoTime() - START_TIME;
}
compareTo
public int compareTo(Delayed o) {
if (this == o) {
return 0;
}
// 首先比较到期时间,到期时间更近的排在前面
// 然后比较id,id小的排在前面
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;
}
}
delayNanos
public long delayNanos() {
// 计算当前还有多少时间到达deadline
// deadlineNanos + START_TIME - current
return Math.max(0, deadlineNanos() - nanoTime());
}
public long delayNanos(long currentTimeNanos) {
// deadline + START_TIME - current
// 指定时间距离deadline还有多少时间
return Math.max(0, deadlineNanos() - (currentTimeNanos - START_TIME));
}
run
下面看下是如何执行的,这里的实现比较巧妙,当任务执行完毕后,如果判断当前任务仍然需要执行,会将自己添加到executor的定时任务队列中
public void run() {
// 判断当前线程是否是eventLoop绑定的线程
assert executor().inEventLoop();
try {
// 当前任务不需要重复
if (periodNanos == 0) {
// 将当前任务的状态设置为不可取消f
if (setUncancellableInternal()) {
// 执行任务
V result = task.call();
// 如果result不为null,将任务的状态设置为返回值,否则设置为SUCCESS
// 并且回调listener
setSuccessInternal(result);
}
} else {
// check if is done as it may was cancelled
// 如果任务没有取消
if (!isCancelled()) {
// 执行任务
task.call();
// 判断executor没有关闭
if (!executor().isShutdown()) {
long p = periodNanos;
// 如果p>0,代表需要重复,并且按照任务的开始时间以一定间隔重复执行
if (p > 0) {
deadlineNanos += p;
} else {
// 如果p<0,代表需要重复,并且按照任务的结束时间
// deadlineNanos = (current - p) - START_TIME,相当于当前时间延迟-p nano执行
deadlineNanos = nanoTime() - p;
}
// 如果当前任务没有取消,将任务添加到executor的定时任务队列中
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);
}
}
cancel
一共有两个版本的cancel,都会取消任务的执行,cancel会另外将任务从定时任务队列中移除
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);
}
AbstractScheduledEventExecutor
重要属性
// 指定如何对定时任务队列中的任务排序
private static final Comparator<ScheduledFutureTask<?>> SCHEDULED_FUTURE_TASK_COMPARATOR =
new Comparator<ScheduledFutureTask<?>>() {
@Override
public int compare(ScheduledFutureTask<?> o1, ScheduledFutureTask<?> o2) {
return o1.compareTo(o2);
}
};
// 定时任务队列
PriorityQueue<ScheduledFutureTask<?>> scheduledTaskQueue;
schedule
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
ObjectUtil.checkNotNull(callable, "callable");
ObjectUtil.checkNotNull(unit, "unit");
if (delay < 0) {
delay = 0;
}
validateScheduled0(delay, unit);
// 使用给定的参数创建一个ScheduledFutureTask
return schedule(new ScheduledFutureTask<V>(
this, callable, ScheduledFutureTask.deadlineNanos(unit.toNanos(delay))));
}
<V> ScheduledFuture<V> schedule(final ScheduledFutureTask<V> task) {
// 如果当前线程和eventLoop绑定的线程是同一个,那么将定时任务直接添加到定时任务队列中
if (inEventLoop()) {
scheduledTaskQueue().add(task);
} else {
// 如果线程不一致,通过提交任务的方法来进行添加
execute(new Runnable() {
@Override
public void run() {
scheduledTaskQueue().add(task);
}
});
}
return task;
}
普通任务添加
这里看下SingleThreadEventExecutor的execute方法
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
boolean inEventLoop = inEventLoop();
// 将待执行任务添加到任务队列中
addTask(task);
if (!inEventLoop) {
startThread();
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
protected void addTask(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
if (!offerTask(task)) {
reject(task);
}
}
final boolean offerTask(Runnable task) {
if (isShutdown()) {
reject();
}
return taskQueue.offer(task);
}