最近項目中遇到不少nio相關知識,之前對這塊接觸得較少,算是我的一個盲區,打算花點時間學習,簡單做一點個人學習總結。
nio(new io)是jdk1.4以後推出的全新io api,相比傳統io方式nio采用了全新的底層i/o模型。傳統io的設計概念是面向流,而nio則是面向塊。簡單點說,傳統i/o是基于位元組的,所有i/o都被視為單個位元組的移動,使用時需先把對象轉換為位元組碼;而nio是面向塊的,以塊為機關處理資料,每個操作會生成或消費一個塊的資料。從設計理念來看,nio的操作粒度要比傳統io大很多,采用這種方式性能有很大提高,但也犧牲了java操作的優雅和簡潔。
java nio 由以下三個核心子產品組成:
selector
channel
buffer

線程的花銷很大,selector幫助開發者突破io的瓶頸,它提供了單線程處理多個 channel的機制。如果你的應用打開了多個連接配接(通道),但每個連接配接的流量都很低,使用selector就會很友善。
這是在一個單線程中使用一個selector處理3個channel的圖示:
要使用selector,得先向selector注冊channel,然後調用它的select()方法。這個方法會一直阻塞到某個注冊的通道有事件就緒。一旦這個方法傳回,線程就可以處理這些事件,事件的例子有如新連接配接進來,資料接收等。
使用selector,先得向selector注冊channel,然後調用它的select()方法,這個方法會一直阻塞直到某個注冊的通道有事件就緒。一旦這個方法傳回,線程就可以處理這些事件。
channel是一個對象,可以通過它讀取和寫入資料,所有的io在nio中都從一個channel 開始。channel中的資料要麼寫入buffer,要麼從buffer讀取。拿 nio 與原來的 i/o 做個比較,channel就像是流。所有資料都通過buffer對象來處理,nio不會将位元組直接寫入通道中,相反,它是将資料寫入包含一個或者多個位元組的緩沖區。同樣,您不會直接從通道中讀取位元組,而是将資料從通道讀入緩沖區,再從緩沖區擷取這個位元組。
通道與流的不同之處在于通道是雙向的,而流隻是在一個方向上移動(一個流必須是 inputstream 或者 outputstream 的子類), 而 通道可以用于讀、寫或者同時用于讀寫。是以,通道可以比流更好地反映底層作業系統的真實情況。特别是在unix模型中,底層作業系統通道是雙向的。
常用的channel:
filechannel:file
datagramchannel:udp
scoketchannel:tcp
serversocketchannel:監聽tcp連接配接
buffer與channel的關系如下圖:
buffer也是nio的一個亮點,傳統的io操作面向資料流,意味着每次從流中讀一個或多個位元組,直至完成,資料沒有被緩存在任何地方。nio操作面向緩沖區,資料從channel讀取到buffer緩沖區,随後在buffer中處理資料。
buffer其實就是一塊緩存區,内部使用位元組數組存儲資料,并維護幾個特殊變量,實作資料的反複利用。
目前buffer的實作類有以下幾種:
bytebuffer
charbuffer
doublebuffer
floatbuffer
intbuffer
longbuffer
shortbuffer
mappedbytebuffer
這些都是抽象類,針對各自的應用場景,下面又有很多子類,功能強大。
一般我們在工作中遇到nio相關的場景時,大多都有封裝好的三方庫,但并不是說就沒必要了解其實作原理,知其然而不知其是以然用着也會心虛。
另外,學習優秀的開源項目是提升個人能力的一個有效方法,研究源碼可以從以下幾個點入手(個人認為):
架構文檔 (在腦中建立架構圖)
示例代碼與單元測試(知道具體接口使用細節)
具體子產品實作代碼(資料結構 - public方法 - private方法)