昨天已經(jīng)看到NioEventLoop的執(zhí)行是在SingleThreadEventExecutor.this.run(); 這個(gè)地方調(diào)用執(zhí)行的,那接下來(lái)我們繼續(xù)看執(zhí)行的過(guò)程
根據(jù)NioEventLoop構(gòu)造函數(shù)中初始化的select策略執(zhí)行select操作

首先調(diào)用select方法輪訓(xùn)io事件,前面我們分析了一個(gè)NioEventLoop對(duì)應(yīng)一個(gè)selector,其實(shí)this.select(this.wakenUp.getAndSet(false));就是通過(guò)selector輪訓(xùn)注冊(cè)到這個(gè)selector上的io事件,這里的wakenUp是標(biāo)示當(dāng)前的selector操作是不是喚醒的狀態(tài),每次進(jìn)行select操作的時(shí)候都會(huì)標(biāo)記為false,也就是說(shuō)要進(jìn)行select操作,并且是未喚醒狀態(tài)。我們看下select方法的執(zhí)行邏輯




避免空輪訓(xùn)的bug,重置selector

看下具體的重置過(guò)程


然后通過(guò)調(diào)用this.processSelectedKeys();處理輪訓(xùn)到的io事件,這個(gè)ioRatio默認(rèn)情況下是50,也就是說(shuō),處理io事件和處理MpscQueue里面的任務(wù)的時(shí)間比是1:1

首先看下NioEventLoop構(gòu)造函數(shù)中selector的初始化過(guò)程

再看openSelector中的邏輯關(guān)于keySet的優(yōu)化過(guò)程





接下來(lái)就是我們的io.netty.channel.nio.NioEventLoop#processSelectedKeys操作


看下processSelectedKey的執(zhí)行邏輯,這里主要就是處理io事件了


最后調(diào)用this.runAllTasks(ioTime * (long)(100 - ioRatio) / (long)ioRatio);來(lái)處理外部線程扔到taskQueue里面的任務(wù),包含普通task和定時(shí)任務(wù)的task;那這個(gè)定時(shí)任務(wù)哪里來(lái)的呢?在這里io.netty.util.concurrent.AbstractScheduledEventExecutor#schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit)
初始化一個(gè)定時(shí)任務(wù),在這里會(huì)封裝成ScheduledFutureTask



再看runAllTask中的邏輯


先看下ScheduledFutureTask任務(wù)的排列方式io.netty.util.concurrent.ScheduledFutureTask#compareTo

獲取最靠前的任務(wù),其實(shí)這是任務(wù)的聚合


接下來(lái)就是獲取任務(wù)并且執(zhí)行任務(wù)


最后總結(jié)下netty在這個(gè)過(guò)程中是如何保證異步串行無(wú)鎖化的?
netty在所有外部線程調(diào)用NioEventLoop執(zhí)行任務(wù)的時(shí)候,通過(guò)InEventLoop判斷得出是不是外部線程,如果InEventLoop返回false,則將一個(gè)個(gè)的任務(wù)封裝成task丟到NioEventLoop中的mpscQueue當(dāng)中去,然后在NioEventLoop的執(zhí)行邏輯中,這些任務(wù)會(huì)被挨個(gè)執(zhí)行;
好了NioEventLoop的執(zhí)行過(guò)程就到這里結(jié)束了