java NIO理解分析與基本使用
阿新 • • 發佈:2020-04-04
我前段時間的一篇部落格[java網路程式設計——多執行緒資料收發並行](https://www.cnblogs.com/buptleida/p/12514450.html)總結了服務端與客戶端之間的收發並行實踐。原理很簡單,就是針對單一客戶端,服務端起兩個執行緒分別負責read和write操作,然後執行緒保持阻塞等待讀寫執行。
事實上,這樣的模式非常糟糕。因為每一個客戶端在服務端需要佔用兩條執行緒,假如有1000個客戶端,則需要2000+條執行緒。cpu需要花費大量的時間進行執行緒上下文切換,造成系統資源浪費。
想要縮減執行緒數量,先要解決阻塞問題。而NIO可以通過IO多路複用將read和write的阻塞給抹去。再配合執行緒池,即可實現用少量的執行緒支撐起上百萬個客戶端的連線。
## 什麼是NIO
#### NIO與IO多路複用
java NIO全稱java non-blocking IO。字面意思即非阻塞式IO。實際上這裡的非阻塞只是巨集觀的說法。
關於IO模式,這裡引一個別人的部落格,介紹了幾種IO模式的區別:
[簡述同步IO、非同步IO、阻塞IO、非阻塞IO之間的聯絡與區別](https://www.cnblogs.com/felixzh/p/10345929.html)
本部落格不再贅述這些,只是想說NIO屬於其中的IO複用模型。(實驗室裡有一本《UNIX網路程式設計》疫情結束回學校一定把這部分好好看看)
多路複用IO模型中,會有一個執行緒去不斷輪詢多個socket的狀態,當socket有讀寫事件時,才來呼叫IO操作。因為是一個執行緒來管理多個socket,系統不需要建立其它執行緒、維護執行緒,只有socket就緒時,才會使用IO資源,所以它大大降低了資源佔用。
java NIO中,使用selector.select()監聽多個通道是否有到達事件,沒有事件就一直阻塞,有事件就呼叫IO進行處理。
#### 三大核心
- 通道(Channel)
- 緩衝區(Buffer)
- 選擇器(Selectors)
詳細介紹如下
![image](http://qiniu.debrisflow.cn/20200404NIO3Bean.png)
## NIO使用舉例
這裡以服務端讀取客戶端訊息的流程為例,介紹NIO的使用(完整內容只有輸入,暫且不管輸出)。畫了一個流程圖,如下所示:
![image](http://qiniu.debrisflow.cn/20200404NIORead.png)
1. 建立selector和ServerSocketChannel,並繫結註冊,用於監聽客戶端連線請求,程式碼如下:
```
selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
// 設定為非阻塞
server.configureBlocking(false);
// 繫結本地埠
server.socket().bind(new InetSocketAddress(port));
// 註冊客戶端連線到達監聽
server.register(selector, SelectionKey.OP_ACCEPT);
```
同時還要建立readSelector和writeSelector。其實執行緒池也是提前建立的,這裡暫且不寫。
```
readSelector = Selector.open();
writeSelector = Selector.open();
```
2. 監聽通道,得到客戶端,並建立SocketChannel,用於監聽後續客戶端訊息
```
//select()方法返回已就緒的通道數
if (selector.select() == 0) {
continue;
}