NioEventLoopGroup對象能夠理解爲一個線程池,內部維護了一組線程,每一個線程負責處理多個Channel上的事件,而一個Channel只對應於一個線程,這樣能夠迴避多線程下的數據同步問題。php
咱們先回顧下 上篇博客的服務器代碼java
// 定義一對線程組 // 主線程組, 用於接受客戶端的鏈接,可是不作任何處理,跟老闆同樣,不作事 EventLoopGroup bossGroup = new NioEventLoopGroup(); // 從線程組, 老闆線程組會把任務丟給他,讓手下線程組去作任務 EventLoopGroup workerGroup = new NioEventLoopGroup(); // netty服務器的建立, 輔助工具類,用於服務器通道的一系列配置 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) //綁定兩個線程組 //省略......
職責
:數組
Acceptor 線程
,負責處理客戶端的請求接入。Connector 線程
,負責註冊監聽鏈接操做位,用於判斷異步鏈接結果。 IO 線程
,監聽網絡讀操做位,負責從 SocketChannel 中讀取報文。定時任務線程
,能夠執行定時任務,例如鏈路空閒檢測和發送心跳消息等。上面的代碼 建立bossGroup及workerGroup時,使用了NioEventLoopGroup的無參構造方法,本篇將今後無參構造入手,詳細分析NioEventLoopGroup的初始化過程。服務器
/** * 一、首先咱們看看NioEventLoopGroup的無參構造方法: * 做用:線程數爲0 */ public NioEventLoopGroup() { this(0); } /** * 二、繼續調用構造函數。 * 做用:指定線程爲0,且Executor爲null */ public NioEventLoopGroup(int nThreads) { this(nThreads, (Executor) null); } /** * 三、繼續調用構造函數 * 做用:此構造方法它會指定selector的輔助類 "SelectorProvider.provider()" */ public NioEventLoopGroup(int nThreads, Executor executor) { this(nThreads, executor, SelectorProvider.provider()); } /** * 四、繼續調用構造函數 * 做用:初始化了一個默認的選擇策略工廠,用於生成select策略 */ public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) { this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE); } /** * 五、繼續調用構造函數 * 做用:指定拒絕策略:RejectedExecutionHandlers.reject() */ public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) { super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject()); }
通過上面一系列的構造方法調用,此時參數值對應以下:網絡
/** * 六、從這裏開始 調用父類 MultithreadEventLoopGroup 的構造函數 * 做用: 就是當指定的線程數爲0時,使用默認的線程數DEFAULT_EVENT_LOOP_THREADS, * 而DEFAULT_EVENT_LOOP_THREAD是在靜態代碼塊中就被執行。 */ protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) { super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args); } /** * 6.1 咱們看下靜態代碼塊 * 做用:到這一步得出關鍵的一點:`若是初始化NioEventLoopGroup未指定線程數,默認是CPU核心數*2`。 */ private static final int DEFAULT_EVENT_LOOP_THREADS; static { DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt( "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)) } /** * 七、繼續調用父類 MultithreadEventLoopGroup 構造函數 * 做用:指定了一個EventExecutor的選擇工廠DefaultEventExecutorChooserFactory, * 此工廠主要是用於選擇下一個可用的EventExecutor */ protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) { this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args); } /** * 八、繼續調用父類 MultithreadEventLoopGroup 構造函數 這裏就是核心代碼 刪除部分非核心代碼 * 做用單獨分析 */ protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { //一、 //executor校驗非空, 若是爲空就建立ThreadPerTaskExecutor, 該類實現了 Executor接口 // 這個executor 是用來執行線程池中的全部的線程,也就是全部的NioEventLoop,其實從 //NioEventLoop構造器中也能夠知道,NioEventLoop構造器中都傳入了executor這個參數。 if (executor == null) { executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); } //二、 //這裏的children數組, 其實就是線程池的核心實現,線程池中就是經過指定的線程數組來實現線程池; //數組中每一個元素其實就是一個EventLoop,EventLoop是EventExecutor的子接口。 children = new EventExecutor[nThreads]; //for循環實例化children數組,NioEventLoop對象 for (int i = 0; i < nThreads; i++) { boolean success = false; //三、 //newChild(executor, args) 函數在NioEventLoopGroup類中實現了, // 實質就是就是存入了一個 NIOEventLoop類實例 children[i] = newChild(executor, args); success = true; } //四、實例化線程工廠執行器選擇器: 根據children獲取選擇器 chooser = chooserFactory.newChooser(children); //五、爲每一個EventLoop線程添加 線程終止監聽器 final FutureListener<Object> terminationListener = new FutureListener<Object>() { @Override public void operationComplete(Future<Object> future) throws Exception { if (terminatedChildren.incrementAndGet() == children.length) { terminationFuture.setSuccess(null); } } }; //六、將children 添加到對應的set集合中去重, 表示只可讀。 Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length); Collections.addAll(childrenSet, children); readonlyChildren = Collections.unmodifiableSet(childrenSet); } } /** * 8.3.1 咱們再來看下 newChild(executor, args) 裏的方法 * 咱們能夠看到 返回的就是一個 NioEventLoop */ @Override protected EventLoop newChild(Executor executor, Object... args) throws Exception { return new NioEventLoop(this, executor, (SelectorProvider) args[0], ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]); }
咱們再回顧總結一下:多線程
1. NioEventLoopGroup初始化時未指定線程數,那麼會使用默認線程數,即 `線程數 = CPU核心數 * 2`; 2. 每一個NioEventLoopGroup對象內部都有一組可執行的`NioEventLoop數組`,其大小是 nThreads, 這樣就構成了一個線程池, `一個NIOEventLoop能夠理解成就是一個線程`。 3. 全部的NIOEventLoop線程是使用相同的 executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler以及是屬於某一個 NIOEventLoopGroup的。這一點從 newChild(executor, args); 方法就能夠看出:newChild()的實現是在NIOEventLoopGroup中實現的。 4. 當有IO事件來時,須要從線程池中選擇一個線程出來執行,這時候的NioEventLoop選擇策略是由GenericEventExecutorChooser實現的,並調用該類的next()方法。 5. 每一個NioEventLoopGroup對象都有一個NioEventLoop選擇器與之對應,其會根據NioEventLoop的個數,動態選擇chooser(若是是2的冪次方,則按位運算,不然使用普通的輪詢)
因此經過上面的分析,咱們得出NioEventLoopGroup主要功能就是爲了建立必定數量的NioEventLoop,而真正的重點就在NioEventLoop中,它是整個netty線程執行的關鍵。異步
有關NioEventLoop能夠參考文章: [netty源碼分析]--EventLoopGroup與EventLoop
相關文章推薦:netty服務端源碼分析之eventloop和eventloopgroupide
若是一我的充滿快樂,正面的思想,那麼好的人事物就會和他共鳴,並且被他吸引過來。一樣,一我的老帶悲傷,倒黴的事情也會跟過來。 ——在本身心情低落的時候,告誡本身不要把負能量帶給別人。(大校12)