1. 程式人生 > >Java NIO:選擇器

Java NIO:選擇器

最近打算把Java網路程式設計相關的知識深入一下(IO、NIO、Socket程式設計、Netty) Java NIO主要需要理解緩衝區、通道、選擇器三個核心概念,作為對Java I/O的補充, 以提升大批量資料傳輸的效率。 學習NIO之前最好能有基礎的網路程式設計知識 [Java I/O流](https://www.cnblogs.com/pepper-0611/p/13735018.html) [Java 網路程式設計](https://www.cnblogs.com/pepper-0611/p/13767359.html) [Java NIO:緩衝區](https://www.cnblogs.com/pepper-0611/p/13767775.html) [Java NIO:通道](https://www.cnblogs.com/pepper-0611/p/13797573.html) 傳統監控多個Socket的Java解決方案是為每一個Socket建立一個執行緒並使執行緒阻塞在read呼叫處, 直到資料可讀。這種方式在系統併發不高時可以正常執行,如果是併發很高的系統就需要建立很多的執行緒(每個連線需要一個執行緒)。過多的執行緒會導致頻繁的上下文切換、且執行緒是系統資源,可建立最大執行緒數是有限制的且遠小於可以建立的網路連線數。 NIO的選擇器就是為了解決這個問題, 選擇器提供了同時詢問多個通道是否準備好執行I/O的能力,比如SocketChannel物件是否還有更多的位元組待讀取, ServerSocketChannel是否有已經到達的客戶端連線。 通過使用選擇器,我們可以在一個執行緒裡監聽多個通道的就緒狀態! ## 核心概念 選擇器 :管理可選擇通道集合&更新可選擇通道的就緒狀態 可選擇通道:所有繼承了SelectableChannel的通道, Socket都是可選擇的,而檔案通道不是,只有可選擇通道可以註冊到選擇器上。 選擇鍵:可選擇通道註冊到選擇器後返回選擇鍵, 所以選擇鍵其實是通道與選擇器註冊關係的一個封裝 三者之間的關係:***可選擇通道***註冊到***選擇器***上,返回***選擇鍵*** ## 選擇器使用 使用選擇器的步驟一般是: 1. 構造選擇器 2. 可選擇通道註冊到選擇器 3. 選擇器選擇(選擇出就緒通道) 4. 對就緒通道進行讀寫操作 5. 重複 2 ~ 4 下面從這幾步進行講解 ### 構造選擇器 使用靜態工廠方法構造(底層使用SelectorProvider建立Selector例項, SelectorProvider支援java spi擴充套件) ```java Selector selector = Selector.open(); ``` ### 可選擇通道註冊到選擇器 只有執行在**非阻塞模式**下的通道可以註冊到選擇器上 註冊的方法定義在SelectableChannel類中, 註冊時需帶上可選擇的操作(四種可選擇操作,定義在SelectionKey中),也可以帶上附件 註冊成功返回選擇鍵,具體API如下: ```java //引數 選擇器 + 可選擇操作 public final SelectionKey register(Selector sel, int ops) //帶附件的版本 public abstract SelectionKey register(Selector sel, int ops, Object att) ``` ### 選擇器選擇 select方法選擇出就緒的通道,把該就緒通道關聯的SelectionKey放到選擇器的selectedKeys集合中(通道就緒指底層Socket已經就緒,執行連線或者讀寫操作時不會阻塞) ### 對就緒通道進行讀寫操作 對就緒通道的讀寫操作見下面Demo ### Demo 寫了一個demo把上面幾步整合起來,程式碼主要兩部分:可選擇通道註冊&選擇器選擇 和 對就緒通道進行讀寫操作 #### 可選擇通道註冊&選擇器選擇 ```java //構造選擇器 Selector selector = Selector.open(); //初始化ServerSocketChannel繫結本地埠並設定為非阻塞模式 ServerSocketChannel ch = ServerSocketChannel.open(); ch.bind(new InetSocketAddress("127.0.0.1", 7001)); ch.configureBlocking(false); //通道註冊到選擇器,並且關心ACCEPT操作(因為是Server) ch.register(selector, SelectionKey.OP_ACCEPT); //一直迴圈, 進行通道就緒狀態的選擇 while (true) { int n = selector.select(); if (n == 0) { continue; }