Netty學習(2):IO模型之NIO初探
NIO 概述
前面說到 BIO 有著建立執行緒多,阻塞 CPU 等問題,因此為解決 BIO 的問題,NIO 作為同步非阻塞 IO模型,隨 JDK1.4 而出生了。
在前面我們反覆說過4個概念:同步、非同步、阻塞、非阻塞。因此,我們就首先用最簡單的語言說一下他們的區別,這裡,我們心裡暫時有個概念即可,在後面的學習過程中,還會對其進行深入的探討學習。
概念對比
首先,我們先要確立一個概念,就是一個IO操作其實是分為兩步的,
- 發起IO請求,即準備資料和區域;
- 實際的IO操作。
而區分一個 IO模型是同步還是非同步,就取決於在進行第2個步驟時,即實際進行讀寫過程中,其是否會阻塞執行緒,如果會阻塞,那麼就是同步的,如果不會(這裡就需要作業系統核心來進行操作了),而是完成後,再通知操作執行緒,那麼其就是非同步的(AIO 就是 OS 來完成檔案讀寫操作,在完成後通過訊息機制來通過執行緒讀寫完畢)。
緊接著,區分一個模型是阻塞還是非阻塞,而是看其在資料沒有準備完畢時,即連線建立後,但還沒有資料過來,請求是需要卡在這等待,還是可以去幹其他的事。
作為上文所述的 BIO 來說,首先因為資料是否讀寫完成,需要其一直在迴圈裡判斷,因此其是同步的,並且如果沒有資料的話,其會一直卡在 read
操作上,不能繼續往下執行,因此 BIO 是一種同步阻塞模型。
而 NIO 的資料讀寫完成與否,也需要自己來判斷,因此也是同步的,但其只有當發生真正讀寫時,才會進行操作,如果沒有準備完畢的話,則可以去做其他的事,因此 NIO 是一個同步非阻塞 IO模型。
總結一下,我們看一個模型是同步還是非同步,其實看的是資料準備完畢後的訊息通知機制;看是阻塞還是非阻塞的,則是看執行緒在請求後的執行緒狀態。
下面就讓我們來看一下 NIO 的基本概念。
NIO基本介紹
- 從 JDK1.4 後,官方引入的 IO 新特性,稱為 NIO,是一個同步非阻塞模型;
- 相關類在
java.nio
包下,有 3大核心元件,Channel,Buffer,Selector; - NIO 是面向快取區程式設計的,或者說可以是面向塊的,而不是面向位元組,因此極大提高了程式設計的靈活性;
- NIO 因為其核心元件和麵向快取區程式設計的特性,因此 資料總是從 Channel 讀取到 Buffer 中,或者從Buffer 讀取到 Channel 中,而 Selector 的作用就是監聽多個 Channel,當 Channel 中有請求需要處理,就進行處理;
- 。。。。。(官網或者搜一下,有很多,在這裡就不多贅述了)
元件介紹
Buffer
快取區本質是一個可以讀寫程式的記憶體塊,我們可以將其簡單理解為一個容器。在其內部還提供了一些方法,方便程式設計人員對其進行操作。
其所有可用子類如 ByteBuffer 等繼承的父類為 Buffer,其有一些公有屬性需要牢記。
屬性 | 說明 |
---|---|
mark | 標記位,在呼叫 mark() 方法後,可以將當前 position 設為標記,後續在呼叫 reset() 方法時,就可以將 position 重置回該位置,如果不設定,則為負數,那麼在呼叫 reset 時,會報錯。(一般不需要設定) |
position | 快取區內將要被寫或讀的節點下標,該值不能為負數,且永遠小於等於 limit |
limit | 快取區內第一個不能寫或讀的節點下標,該值不能為負數,且永遠小於等於 capacity |
capacity | 快取區被建立時指定的大小,該值不能為負數,且無法更改 |
mark <= position <= limit <= capacity
Buffer 程式碼演示
IntBuffer intBuffer = IntBuffer.allocate(5);
for (int i = 1; i <= intBuffer.capacity(); i++) {
intBuffer.put(i);
}
// 翻轉,讀寫切換
intBuffer.flip();
while (intBuffer.hasRemaining()) {
System.out.println(intBuffer.get());
}
intBuffer.position(0);
for (int i = 1; i <= intBuffer.capacity(); i++) {
intBuffer.put(i * i);
}
intBuffer.flip();
while (intBuffer.hasRemaining()) {
System.out.println(intBuffer.get());
}
}
從上述程式碼中,可以看到 快取區是一個可讀可寫的區域,且像陣列容器一樣可以在裡面通過下標的方式進行移動,從而修改指定地方內容。
Channel
Channel 是流中的一個元件,在使用過程中,通過流來生成。
Channel 是 NIO 中的一個介面,其實現類中,我們使用的比較多的有FileChannel
,ServerSocketChannel
,SocketChannel
,DatagramChannel
,其中 FileChannel 用於檔案的讀寫,ServerSocketChannel 和 SocketChannel 用於TCP資料讀寫,DatagramChannel 用於 UDP 資料讀寫。
這裡只描述一些 Channel 的一些簡單概念,在後續文章:《檔案操作》中,再用程式碼詳細展示 Channel 的作用。
Selector
selector 解決的是 BIO 的執行緒阻塞和一個請求就需要建立一個執行緒的問題。selector 可以監控多個 Channel,如果對應的 Channel 有 Event 發生,就可以獲取對應 Event,然後根據獲取 Event 的不同去進行不同的處理邏輯,這樣就不必每個請求都建立執行緒,並且只有當真正有讀寫事件發生時,才會進行操作。
也是隻描述 Selector 的一些簡單概念,在後續文章:《網路程式設計》中,再用程式碼詳細展示 Selector 的作用。
總結
在本文中,我們初步介紹了 NIO 的概念,以及其的 3個核心元件:Buffer,Channel 和 Selector。
在後續的文章中,我們將對其分別進行介紹,通過 NIO 來逐步引入 Netty 的實現。
本文中程式碼已上傳到 GitHub 上,地址為 https://github.com/wb1069003157/nettyPre-research ,歡迎大家來討論,探討。
文章在公眾號「iceWang」第一手更新,有興趣的朋友可以關注公眾號,第一時間看到筆者分享的各項知識點,謝謝!筆芯!