/** * 有任务提交过来希望被执行 * EventLoop本质是跟Java的线程1:1映射的 而且EventLoop只有一个线程 所以意味着这个线程只会启动一次 * 创建线程的时机就是在任务提交过来的时候 * - java向cpp创建线程 * - cpp向os创建线程 并且告诉os这个线程的entry point是哪儿 对应着java的一个函数 * - 等线程被cpu调度起来后就会顺着entry point回调到java里面的这个函数 * 所以在EventLoop中用inEventLoop作为标识线程是不是已经创建了 保证只有一个线程 不会重复创建 */ privatevoidexecute(Runnable task, boolean immediate) { /** * NioEventLoop只有一个线程 且它的阻塞点只有在IO多路复用器操作上 * 因此当前添加任务的线程 * - NioEventLoop线程自己给自己添加任务 说明它压根没有被阻塞 而且肯定已经处于运行中状态 * - 这个线程已经被创建执行 那么这个新添加的任务被放到了非IO任务队列中 迟早会被取出来执行 * - 不是NioEventLoop线程 是其他线程往NioEventLoop添加任务 * - 如果NioEventLoop线程还没被创建执行 那么相当于任务裹挟着线程进行延迟创建并执行任务 * - 非IO任务队列没有任务 也没有IO事件到达时 NioEventLoop线程迟早会阻塞在复用器上 * - 阻塞期间有IO事件到达 退出select阻塞继续工作 * - 有定时任务还可能超时退出select NioEventLoop线程继续工作 * - 没有定时任务就永远阻塞 唤醒的方式 只有外部线程往NioEventLoop添加新任务触发selector复用器的wakeup() */ booleaninEventLoop=super.inEventLoop(); this.addTask(task); // 添加任务到taskQueue中 如果任务队列已经满了 就触发拒绝策略(抛异常) if (!inEventLoop) { // NioEventLoop线程创建启动的时机就是提交进来的第一个异步任务 在这个方法里面创建线程并为线程的调度指定好entry point this.startThread(); if (this.isShutdown()) { booleanreject=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(); } }
case SelectStrategy.BUSY_WAIT: // -3 // fall-through to SELECT since the busy-wait is not supported with NIO
case SelectStrategy.SELECT: // -1 任务队列为空 将线程阻塞在复用器上 唤醒时机有两种情况(阻塞期间有IO事件到达 阻塞指定事件后主动结束阻塞开始执行定时任务) longcurDeadlineNanos=super.nextScheduledTaskDeadlineNanos(); // 定时任务队列中下一个待执行定时任务还有多久可以被唤醒执行 -1表示没有定时任务可以执行 if (curDeadlineNanos == -1L) curDeadlineNanos = NONE; // nothing on the calendar // 边界情况 没有定时任务要执行 this.nextWakeupNanos.set(curDeadlineNanos); // 下一次啥时候将线程唤醒 try { if (!super.hasTasks()) strategy = this.select(curDeadlineNanos); // select()方法阻塞 超时时间是为了执行可能存在的定时任务 如果没有定时任务就将一直阻塞在复用器的select()操作上等待被唤醒 } finally { // This update is just to help block unnecessary selector wakeups // so use of lazySet is ok (no race condition) nextWakeupNanos.lazySet(AWAKE); } // fall through default: } } catch (IOException e) { // If we receive an IOException here its because the Selector is messed up. Let's rebuild // the selector and retry. https://github.com/netty/netty/issues/8566 rebuildSelector0(); selectCnt = 0; handleLoopException(e); continue; }