NioEventLoop是netty中用来执行任务、从channel中读取数据的执行者,在创建NioEventLoopGroup时,会创建多个NioEventLoop来绑定多个channel。NioEventLoop也是netty中的核心组件,理解了NioEventLoop的设计之后,理解NioEventLoopGroup就非常好理解了。
EventLoop
EventLoop接口继承了ExecutorService接口,表明这也是个线程池。当一个Channel注册完成之后,EventLoop就会处理这个channel的所有I/O操作。通常来说,一个EventLoop会处理多个Channel,但也取决于实现的细节和内部。EventLoop接口还继承了EventLoopGroup和OrderedEventExecutor两个接口,OrderedEventExecutor接口表明这个类会有序地执行被提交的任务,对于EventLoopGroup的特性则会在以后进行介绍。
NioEventLoop
NioEventLoop是SingleThreadEventLoop类的子类,实现了EventLoop。NioEventLoop将channel注册到多路复用器(selector)上并处理事件循环。
NioEventLoop中定义了一个常量SELECTOR_AUTO_REBUILD_THRESHOLD使用来解决Linux下epoll bug的,这个值在静态代码块中赋值,可以通过系统参数io.netty.selectorAutoRebuildThreshold来进行设置,默认为512:
private static final int MIN_PREMATURE_SELECTOR_RETURNS = 3;
private static final int SELECTOR_AUTO_REBUILD_THRESHOLD;
// Workaround for JDK NIO bug.
//
// See:
// - http://bugs.sun.com/view_bug.do?bug_id=6427854
// - https://github.com/netty/netty/issues/203
static {
//省略部分代码
// 设置SELECTOR_AUTO_REBUILD_THRESHOLD,当事件循环中select次数达到SELECTOR_AUTO_REBUILD_THRESHOLD这个值时,会进行rebuildSelector
int selectorAutoRebuildThreshold = SystemPropertyUtil.getInt("io.netty.selectorAutoRebuildThreshold", 512);
if (selectorAutoRebuildThreshold < MIN_PREMATURE_SELECTOR_RETURNS) {
selectorAutoRebuildThreshold = 0;
}
SELECTOR_AUTO_REBUILD_THRESHOLD = selectorAutoRebuildThreshold;
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.noKeySetOptimization: {}", DISABLE_KEY_SET_OPTIMIZATION);
logger.debug("-Dio.netty.selectorAutoRebuildThreshold: {}", SELECTOR_AUTO_REBUILD_THRESHOLD);
}
}
NioEventLoop中还有有以下的非静态成员变量:
/**
* The NIO {@link Selector}.
*/
private Selector selector;
private Selector unwrappedSelector;
private SelectedSelectionKeySet selectedKeys;
private final SelectorProvider provider;
// nextWakeupNanos is:
// AWAKE when EL is awake
// NONE when EL is waiting with no wakeup scheduled
// other value T when EL is waiting with wakeup scheduled at time T
private final AtomicLong nextWakeupNanos = new AtomicLong(AWAKE);
private final SelectStrategy selectStrategy;
private volatile int ioRatio = 50;
private int cancelledKeys;
private boolean needsToSelectAgain;
其中
- selector是被包装后的多路复用器
- unwrappedSelector是未被包装的多路复用器
- selectedKeys是selector收集到的事件的集合,内部包含了一个SelectionKey数组
- provider是用来获取Selector的提供者
- nextWakeupNanos AWAKE表示这个EventLoop是活跃的,None表时当前EventLoop在等待并且没有计划中的唤醒任务,其他值T表时当前EventLoop正在等待并且会在时间T的时候被计划唤醒,这个T是一个纳秒
- selectStrategy 是select策略的计算器,其中的calculateStrategy方法用来计算当前的策略,默认的实现类DefaultSelectStrategy的calculateStrategy方法中是先判断任务队列中是否有任务,如果没有任务就返回SelectStrategy.Select(-2),表示需要进行阻塞地去执行selector的select方法,否则就直接执行selector的selectNow方法,获取当前有多少个事件作为结果,如果calculateStrategy方法的返回值大于零,那么就是当前有多少个事件,如果小于零,-1表示重新进行一次操作,-2表示需要进行一次阻塞的select操作,-3表示需要进行一次非阻塞的select操作,NIO模型下-3是不支持的
- ioRatio是事件循环中需要花费在I/O上的时间的比率,值在0-100之间,值越大说明任务的I/O越密集,值越小说明计算越密集
- cancelledKeys被移除的SelectionKey的数量
- needsToSelectAgain是否需要再次select
NioEventLoop只有一个protected修饰的构造方法:
/**
* 代码片段2 NioEventLoop中的唯一构造方法
*/
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
EventLoopTaskQueueFactory queueFactory) {
super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
rejectedExecutionHandler);
this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
final SelectorTuple selectorTuple = openSelector();
this.selector = selectorTuple.selector;
this.unwrappedSelector = selectorTuple.unwrappedSelector;
}
在构造函数中,会给provider,selectStrategy,selector,unwrappedSelector进行赋值。其中provider和selectStrategy是构造参数中的,selector和unwrappedSelector是从构造的SelectorTuple对象中获取。selectorTuple对象通过openSelecter方法创建,openSelcetor方法会调用SelectorProvider的openSelector方法创建一个Selector,在NioEventLoop的openSelector方法中还会通过反射将创建的selector对象中的selectedKeys属性和publicSelectedKeys属性使用新创建的SelectedSelectionKeySet对象进行赋值,同时也将这个对象赋值给当前NioEventLoop的selectedKeys属性。
private SelectorTuple openSelector() {
final Selector unwrappedSelector;
try {
unwrappedSelector = provider.openSelector();
} catch (IOException e) {
throw new ChannelException("failed to open a new selector", e);
}
// 省略部分代码
Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction
NioEventLoop中最重要的方法是run方法,由于方法内容很多,因此就直接在代码中进行注释,部分非关键代码也会进行省略:
@Override
protected void run() {
int selectCnt = 0;
//这是一个死循环 也就是会一直去获取事件
for (;;) {
try {
int strategy;
try {
//执行策略计算器的计算策略的方法,如果hasTask方法为true,就是说队列里面有任务,那么selector会立刻执行一次select方法,得到的int结果即为strategy
strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
switch (strategy) {
//CONTINUE和BUSY_WAIT都不会被走到
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
// fall-through to SELECT since the busy-wait is not supported with NIO
case SelectStrategy.SELECT:
//走到SELECT的话,说明上面执行的hasTasks为false
//计算这一次执行任务的时间在多少纳秒后
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // nothing on the calendar
}
//设置当前NioEventLoop下一次select操作在curDeadlineNanos纳秒,这个纳秒数是基于io.netty.util.concurrent.ScheduledFutureTask类第一次被调用时其中的静态变量START_TIME被赋的值(当时的时间System.nanosTime())
nextWakeupNanos.set(curDeadlineNanos);
try {
if (!hasTasks()) {
//如果curDeadlineNanos是NONE或者当前时间距离curDeadlineNanos不到5微秒,selector会立刻执行select操作,否则会阻塞到deadline(curDeadlineNanos - 5微秒,然后在按毫秒取整,也就是如果是6微秒,那么会阻塞到1毫秒之后)这个时间点再去select,得到的结果就是strategy
strategy = select(curDeadlineNanos);
}
} finally {
// This update is just to help block unnecessary selector wakeups
// so use of lazySet is ok (no race condition)
//这个时候select操作已经执行了,将当前NioEventLoop设置为活跃的(从select操作的阻塞中wakeup)
nextWakeupNanos.lazySet(AWAKE);
}
// fall through
default:
// 如果hasTasks为true,则不会执行上面的逻辑
} catch (IOException e) {
//省略异常处理
}
//上面的try代码块中strategy的结果就是执行一次select操作的结果,也就是事件的数量
//select的次数加一(不管hasTasks是否哦为true都会执行一次select操作,如果是CONTINUE的话,则不会走到这里而是直接下一次循环,并且CONTINUE这个变量除了这个方法的case中也没有其他地方用到过),重置cancelledKeys和needsToSelectAgain
// selectCnt加一 如果连续512次加一之后没有被重置为0 ,那么netty会认为进入空转了
selectCnt++;
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
boolean ranTasks;
if (ioRatio == 100) {
//如果IO比例为100就先执行processSelectedKeys,最后执行runAllTasks。processSelectedKeys方法会获取selectedKeys中的每个SelectionKey对象的attachment属性,这个属性如果是一个channel,就从channel中执行对应事件的逻辑(read,write,connect等),如果selecttedKeys为空会直接return。
try {
if (strategy > 0) {
processSelectedKeys();
}
} finally {
// Ensure we always run tasks.
//依次从scheduledTaskQueue中取出task并执行,最后依次从tailTask中取出task并执行
ranTasks = runAllTasks();
}
} else if (strategy > 0) {
//如果IO比例小于100 且 strategy>0
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
//根据io比例和io所用的时间计算出任务所需的时间
final long ioTime = System.nanoTime() - ioStartTime;
//从scheduledTaskQueue中依次取出task放入taskQueue中,如果放入失败,就讲这个task再次放入scheduledTaskQueue中病结束循环。然后依次从taskQueue中取出task,每执行64(因为执行nanosTime()方法相对昂贵)个task后会判断是否已经超出执行任务的时间,如果超出了会结束循环,最后依次将tailTasks中的task执行完
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
} else {
//参数为0的话就是最多执行64个task,也就是最小的task数量就返回
ranTasks = runAllTasks(0); // This will run the minimum number of tasks
}
if (ranTasks || strategy > 0) {
// 如果一次select操作后有任务被执行或者selcet的结果大于0,那么selectCnt会被重置
if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {
logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
selectCnt - 1, selector);
}
selectCnt = 0;
}
// unexpectedSelectorWakeup会根据select的次数是否大于SELECTOR_AUTO_REBUILD_THRESHOLD,如果大于SELECTOR_AUTO_REBUILD_THRESHOLD则会进行rebuildSelector,rebuildSelector 方法会重新构造一个Seletor并将现有的channel迁移到新的Selector上
// 这是为了处理linux下的epoll bug,nio中是通过selector轮询io事件,selector的select方法会一直阻塞或者超时,linux有时候会出现问题,有时候即使没有io事件到达或超时selector也会返回,会导致线程进入死循环从而cup load 100%,netty定义了一个selector空轮询的次数阈值SELECTOR_AUTO_REBUILD_THRESHOLD默认为512,当selectCnt超过这个值会rebuildSelector
else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
selectCnt = 0;
}
} catch (CancelledKeyException e) {
//省略部分代码
} catch (Throwable t) {
handleLoopException(t);
}
//省略部分代码
}
}
SingleThreadEventLoop
SingleThreadEventLoop是一个抽象类,继承了SingleThreadEventExecutor类,同时是NioEventLoop的父类,它也实现了实现了EventLoop接口。顾名思义,SingleThreadEventLoop使用单线程处理被提交的任务。
SingThreadEventLoop中有一个成员属性tailTasks:
private final Queue tailTasks;
tailTasks是一个存储了Runnable类型对象的一个队列,从名称也可以看出来tailTasks是尾部的任务,tailTasks中的task会在当前或者下一次时间循环中最后别执行(从上文的代码分析中也可以看出)。
提供了一个public修饰的方法executeAfterEventLoopIteration,这个方法是用来添加一个task在当前或者下一次事件循环之后执行的,因为这个方法的权限修饰符是public,所以在使用netty的时候就可以调用这个方法来添加当前或者下一次事件循环之后执行的任务。而一般情况下tailTasks是不会被使用到的。
/**
* Adds a task to be run once at the end of next (or current) {@code eventloop} iteration.
*
* @param task to be added.
*/
@UnstableApi
public final void executeAfterEventLoopIteration(Runnable task) {
ObjectUtil.checkNotNull(task, "task");
if (isShutdown()) {
reject();
}
if (!tailTasks.offer(task)) {
reject(task);
}
if (!(task instanceof LazyRunnable) && wakesUpForTask(task)) {
wakeup(inEventLoop());
}
}
同样还提供了删除task的方法:
/**
* Removes a task that was added previously via {@link #executeAfterEventLoopIteration(Runnable)}.
*
* @param task to be removed.
*
* @return {@code true} if the task was removed as a result of this call.
*/
@UnstableApi
final boolean removeAfterEventLoopIterationTask(Runnable task) {
return tailTasks.remove(ObjectUtil.checkNotNull(task, "task"));
}
SingleThreadEventLoop还重写了父类SingleThreadEventExecutor的hasTasks方法,如果父类中的taskQueue不为空或自身的tailTasks不为空则会返回true:
@Override
protected boolean hasTasks() {
return super.hasTasks() || !tailTasks.isEmpty();
}
SingleThreadEventExecutor
SingleThreadEventExecutor是SingleThreadEventLoop的父类,这是一个抽象类,继承自AbstractScheduledEventExecutor,实现了OrderedEventExecutor接口(SingleThreadEventExecutor没有实现EventLoop接口,所以SingleThreadEventLoop相比SingleThreadEventExecutor而言其特点就是实现了EventLoop接口,也就是说SingleThreadEventLoop处理事件是循环去处理的,不过EventLoop接口并没有定义循环处理的方法,而只是语义上规定进行循环处理事件。因为SingleThreadEventLoop是循环的,所以SingleThreadEventLoop也就可以定于在循环的结尾执行tailTasks中的任务)。
SingleThreadEventExecutor中含有以下一些重要的成员变量:
// 任务队列
private final Queue taskQueue;
// 执行任务的线程,会使用下面的executor来启动,并将thread设置为executor中执行的线程
private volatile Thread thread;
//线程的一些属性
@SuppressWarnings("unused")
private volatile ThreadProperties threadProperties;
//执行器,用来启动
private final Executor executor;
//上面的thread是否被interrupte了
private volatile boolean interrupted;
//最大的任务数量
private final int maxPendingTasks;
//达到最大任务数量之后的拒绝策略
private final RejectedExecutionHandler rejectedExecutionHandler;
//上一次执行的时间
private long lastExecutionTime;
//这个SingleThreadEventExecutor执行器的状态,刚初始化的时候是未启动
@SuppressWarnings({ "FieldMayBeFinal", "unused" })
private volatile int state = ST_NOT_STARTED;
SingleThreadEventExecutor执行任务的方法是execute方法和lazyExecute方法,这两个方法最终都调用了另一个私有的重载的execute方法:
@Override
public void execute(Runnable task) {
ObjectUtil.checkNotNull(task, "task");
execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
}
@Override
public void lazyExecute(Runnable task) {
execute(ObjectUtil.checkNotNull(task, "task"), false);
}
private void execute(Runnable task, boolean immediate) {
//判断当前线程是否是执行任务的线程(成员变量thread)
boolean inEventLoop = inEventLoop();
//将task加入任务队列中
addTask(task);
if (!inEventLoop) {
// 如果当前线程不是执行任务的线程,就执行startThread方法,做这个判断是因为在执行任务的线程中也会使用execute提交任务
startThread();
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
// The task queue does not support removal so the best thing we can do is to just move on and
// hope we will be able to pick-up the task before its completely terminated.
// In worst case we will log on termination.
}
if (reject) {
reject();
}
}
}
if (!addTaskWakesUp && immediate) {
wakeup(inEventLoop);
}
}
在execute(Runnable task, boolean immediate)这个方法中会调用startThread方法,下面看下startThread方法:
private void startThread() {
if (state == ST_NOT_STARTED) {
//如果线程状态没有启动就会将状态设为已经启动,病执行doStartThread方法
//如果线程已经启动了,就什么都不会做,上面的execute(Runnable task, boolean immediate)方法也仅仅只是将task放入任务队列中去
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
boolean success = false;
try {
doStartThread();
success = true;
} finally {
if (!success) {
//没有成功的话就将状态设为未启动
STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);
}
}
}
}
}
线程状态没有启动的话,就调用doStartThread方法,通常来说doStratThread方法只会被调用一次:
private void doStartThread() {
assert thread == null;
// 通过executor这个线程池来执行一个Runnable对象,这个线程池中执行这个Runnable的线程就是工作线程
executor.execute(new Runnable() {
@Override
public void run() {
//将成员变量thread设置为当前线程
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
//这里最终会调用子类中的run方法,在NioEventLoop中就是循环select获取事件,并执行任务,可参考上文介绍的NioEventLoop中run方法
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
//省略部分代码
}
}
});
}
根据以上的代码可以发现,当使用NioEventloop执行任务时,会将任务先放在队列中,如果是第一次启动,会先启动线程,在这个线程中会不断地循环获取事件封装任务放入队列中,并从中任务队列中获取任务执行。这就是NioEventLoop的执行的一系列流程。
AbstractScheduledEventExecutor
AbstractScheduledEventExecutor是一个抽象类,是SingleThreadEventExecutor的父类,是一个支持定时任务的执行器。AbstractScheduledEventExecutor中维护了一个优先队列scheduledTaskQueue用来存放定时任务,以及一个long类型的nextTaskId表示下一个定时任务的id:
PriorityQueue> scheduledTaskQueue;
long nextTaskId;
scheduledTaskQueue中存放的类型为ScheduledFutureTask
上面NioEventLoop的run方法中中调用nextScheduledTaskDeadlineNanos方法来获取下一个定时任务的时间作为select操作的超时时间,其定义如下:
/**
* Return the deadline (in nanoseconds) when the next scheduled task is ready to be run or {@code -1}
* if no task is scheduled.
*/
protected final long nextScheduledTaskDeadlineNanos() {
//获取下一次定时任务
ScheduledFutureTask> scheduledTask = peekScheduledTask();
//返回下一次定时任务的deadlineNanos
return scheduledTask != null ? scheduledTask.deadlineNanos() : -1;
}
AbstractScheduledEventExecutor主要是实现了父类中定义的一些schedule方法:
@Override
public ScheduledFuture> schedule(Runnable command, long delay, TimeUnit unit) {
ObjectUtil.checkNotNull(command, "command");
ObjectUtil.checkNotNull(unit, "unit");
if (delay < 0) {
delay = 0;
}
validateScheduled0(delay, unit);
return schedule(new ScheduledFutureTask(
this,
command,
deadlineNanos(unit.toNanos(delay))));
}
@Override
public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) {
ObjectUtil.checkNotNull(callable, "callable");
ObjectUtil.checkNotNull(unit, "unit");
if (delay < 0) {
delay = 0;
}
validateScheduled0(delay, unit);
return schedule(new ScheduledFutureTask(this, callable, deadlineNanos(unit.toNanos(delay))));
}
@Override
public ScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
ObjectUtil.checkNotNull(command, "command");
ObjectUtil.checkNotNull(unit, "unit");
if (initialDelay < 0) {
throw new IllegalArgumentException(
String.format("initialDelay: %d (expected: >= 0)", initialDelay));
}
if (period <= 0) {
throw new IllegalArgumentException(
String.format("period: %d (expected: > 0)", period));
}
validateScheduled0(initialDelay, unit);
validateScheduled0(period, unit);
return schedule(new ScheduledFutureTask(
this, command, deadlineNanos(unit.toNanos(initialDelay)), unit.toNanos(period)));
}
@Override
public ScheduledFuture> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
ObjectUtil.checkNotNull(command, "command");
ObjectUtil.checkNotNull(unit, "unit");
if (initialDelay < 0) {
throw new IllegalArgumentException(
String.format("initialDelay: %d (expected: >= 0)", initialDelay));
}
if (delay <= 0) {
throw new IllegalArgumentException(
String.format("delay: %d (expected: > 0)", delay));
}
validateScheduled0(initialDelay, unit);
validateScheduled0(delay, unit);
return schedule(new ScheduledFutureTask(
this, command, deadlineNanos(unit.toNanos(initialDelay)), -unit.toNanos(delay)));
}
这些方法都最终构造一个ScheduledFutureTask对象,并且调用private修饰的schedule(final ScheduledFutureTask
private ScheduledFuture schedule(final ScheduledFutureTask task) {
if (inEventLoop()) {
scheduleFromEventLoop(task);
} else {
final long deadlineNanos = task.deadlineNanos();
// task will add itself to scheduled task queue when run if not expired
if (beforeScheduledTaskSubmitted(deadlineNanos)) {
execute(task);
} else {
lazyExecute(task);
// Second hook after scheduling to facilitate race-avoidance
if (afterScheduledTaskSubmitted(deadlineNanos)) {
execute(WAKEUP_TASK);
}
}
}
return task;
}
在schedule(final ScheduledFutureTask
private long deadlineNanos;
/* 0 - no repeat, >0 - repeat at fixed rate, <0 - repeat with fixed delay */
private final long periodNanos;
- deadlineNanos,task的截止时间(纳秒)
- periodNanos,任务循环的时间段(纳秒),如果等于0则不循环,如果大于定则按照固定的频率循环,如果小于0则按照固定的延迟循环(固定频率下一次执行的时间是执行前的时间加上时间段的时长,固定延迟是执行完之后的时间加上时间段的时长)
ScheduledFutureTask父类中还有EventExecutor类型的executor和Runnable类型的task两个成员变量,分别是执行任务的执行器和需要被定时执行的任务本身。
ScheduledFutureTask 的run方法中体现了是如何实现循环的:
public void run() {
assert executor().inEventLoop();
try {
if (delayNanos() > 0L) {
// Not yet expired, need to add or remove from queue
if (isCancelled()) {
scheduledExecutor().scheduledTaskQueue().removeTyped(this);
} else {
scheduledExecutor().scheduleFromEventLoop(this);
}
return;
}
//不循环,直接调用runTask,runTask方法会执行task中的run方法
if (periodNanos == 0) {
if (setUncancellableInternal()) {
V result = runTask();
setSuccessInternal(result);
}
} else {
// check if is done as it may was cancelled
if (!isCancelled()) {
//执行task
runTask();
if (!executor().isShutdown()) {
if (periodNanos > 0) {
//如果按照固定频率,那么这次循环的deadline加上时间段的时长就是下次循环的deadline
deadlineNanos += periodNanos;
} else {
//如果按照固定延迟,就是现在的时间加上时间段的时长,因为这边periodNanos是小于0的,所以这里是减去
deadlineNanos = nanoTime() - periodNanos;
}
//最后因为要循环执行,所以在这里再次将this对象添加到taskQueue中
if (!isCancelled()) {
scheduledExecutor().scheduledTaskQueue().add(this);
}
}
}
}
} catch (Throwable cause) {
setFailureInternal(cause);
}
}
ScheduledFutureTask类型的对象本身就是用来作为任务被执行,ScheduledFutureTask中也包含了一个定时执行的任务(都是Runnable对象)。不管是定时的还是非定时的任务都会放在taskQueue中,它们的run方法都是在NioEventLoop的run方法中调用runAllTasks时循环获取taskQueue中的任务然后被调用。