netty原始碼解解析(4.0)-4 執行緒模型-概覽
netty執行緒體系概覽
netty的高併發能力很大程度上由它的執行緒模型決定的,netty定義了兩種型別的執行緒:
I/O執行緒: EventLoop, EventLoopGroup。一個EventLoopGroup包含多個EventLoop, 每個Channel會被註冊到一個,一個EventLoop中, 一個EventLoop可以包含多個Channel。Channel的Unsafe例項的方法必須要在EventLoop中執行(netty中明確指明的不需要在I/O執行緒中執行的幾個方法除外,前面的章節中有詳細的講解)。
業務執行緒: EventExecutor, EventExecutorGroup。一個EventExecutorGroup包含多個EventExecutor。當用戶向Channel的pipeline註冊一個ChannelHandler時,可以指定一個EventExecutorGroup,這個ChannelHanndler的所有方法都會被放到EventExecutorGroup的中的一個EventExecutor中執行。 當用戶沒有為這個ChannelHandler明確指定EventExecutorGroup時,這個ChannelHandler會被放到Channel所屬的EventLoop中執行。
為了能對netty的執行緒體系有一個整體的認識,筆者提供提供了一張執行緒的派生體系圖供大家參考:

有上圖我們可以得出這樣幾個有用的結論:
- netty的執行緒體系都是由EventExecutorGroup派生而來 而EventExecutorGroup派生自JDK的ScheduleExecutorService, 這表明netty的執行緒體系是對JDK Executor框架的擴充套件。
- netty核心的執行緒體系中,只提供了業務執行緒的最終實現: DefaultEventExecutorGroup, DefaultEvnetExecutor這兩個是可以直接拿來用的。
- netty核心的執行緒體系中,為使用者提供了I/O執行緒的框架: MultithreadEventLoopGroup, SingleThreadEventLoop, 具體I/O相關部分留給子類實現。
- 預設提供了用於建立執行緒的工廠類。
netty線性體系與JDK執行緒體系的對比
如果你熟悉java jdk, 就應該知道,jdk已經為開發者提供了一整套功能強大的基於多執行緒的Executor。那麼問題來了: netty為什麼要搞一套執行緒模型,有這個必要嗎? 回答這個問題之前,我們先來對比一下兩者有什麼不同。
JDK的Executor框架
ThreadPoolExecutor是JDK Exector框架的核心實現,我們使用jdk的Executor框架,主要就是使用這個類,下面看一下這個類的實現原理

netty的EventExecutorGroup框架
MultithreadEventExecutorGroup是EventExecutorGroup框架的核心實現,這個型別的實現原理如下:

為了方便描述,先定義幾個簡稱
ThreadPoolExecuto: TPE
MultithreadEventExecutorGroup: MEG
SingleThreadEventExecutor: STE
接下來,對比一下TPE和MEG
執行緒管理: TPE負責管理執行緒,根據傳入的引數,執行過程中動態調節執行緒數,它也可以讓執行緒一直保持在一個穩定的數量。MEG不負責管理執行緒,它只負責建立指定數量的STE, 每個STE只維護一個執行緒,保證有且只有一個執行緒。
任務排隊: TPE維護一個所有執行緒共用的任務佇列,所有執行緒都從同一佇列中取任務。MEG沒任務佇列,它只負責把任務派發到一個STE, 預設的派發策略是輪詢。每個STE維護一個私有的任務佇列,STE會把任務放入私有的佇列中排隊,這佇列只有STE維護的執行緒才能消費。
任務提交和執行: TPE把任務當成無關聯的獨立任務執行,不保證任務的執行順序和execute的呼叫順序一致, TPE認為任務的順序不重要。MEG提交任務的方式有兩種, (1)直接呼叫MEG的execute方法提交任務,這個方式,和TPE一樣,不關心任務的執行順序;(2)先從MEG中取出一個STE,然後呼叫STE的excute,這種方式任務的執行順序和execute呼叫順序一致。
效能: TPE使用共用的佇列排隊,在高併發環境下會導致BlockingQueue頻繁的鎖碰撞,進而導致大量執行緒切換開銷,MEG中由於佇列是隻有一個執行緒消費,BlockingQueue鎖碰撞機會比TPE小很多,執行緒切換開銷也比TPE小很多,因此,可以得出結論,如果任務本身不會導致執行緒阻塞,MEG效能比TPE高, 否則MEG沒有優勢。
到這裡已經可以回答前面提出的問題了: MEG把任務當成事件來看待,每個事件和特定的Channel關聯(這一點由EventLoopGroup介面體現, 它定義了一個register(Channel channel)方法), 而一個特定Channel上觸發的一系列事件,處理順序和觸發順序必須要一致,如: 在Channel上先後觸發了connect, read, close事件,如果業務上要求收到close事件後不再處理read事件, 如果執行先後順序不能保證,很有可能執行不到read的業務。這種類似業務場景在基於TCP協議的伺服器中很常見,這一點TPE不能支援,而MEG能夠很好地支援這些對任務執行順序有要求的場景。這就是netty要另外設計自己的執行緒模型的主要原因。
(注:為了能幫助讀者更好地解本篇內容,接下來會補充一篇ThreadPoolExecutor程式碼解析的文章)