Buffer:與Channel進行交互,數據是從Channel讀入緩衝區,從緩衝區寫入Channel中的
flip方法 : 反轉此緩衝區,將position給limit,然後將position置為0,其實就是切換讀寫模式
clear方法 :清除此緩衝區,將position置為0,把capacity的值給limit。
rewind方法 : 重繞此緩衝區,將position置為0
DirectByteBuffer可減少一次系統空間到用戶空間的拷貝。但Buffer創建和銷燬的成本更高,不可控,通常會用內存池來提高性能。直接緩衝區主要分配給那些易受基礎系統的本機I/O 操作影響的大型、持久的緩衝區。如果數據量比較小的中小應用情況下,可以考慮使用heapBuffer,由JVM進行管理。
Channel:表示 IO 源與目標打開的連接,是雙向的,但不能直接訪問數據,只能與Buffer 進行交互。通過源碼可知,FileChannel的read方法和write方法都導致數據複製了兩次!
Selector可使一個單獨的線程管理多個Channel,open方法可創建Selector,register方法向多路複用器器註冊通道,可以監聽的事件類型:讀、寫、連接、accept。註冊事件後會產生一個SelectionKey:它表示SelectableChannel 和Selector 之間的註冊關係,wakeup方法:使尚未返回的第一個選擇操作立即返回,喚醒的
原因是:註冊了新的channel或者事件;channel關閉,取消註冊;優先級更高的事件觸發(如定時器事件),希望及時處理。
Selector在Linux的實現類是EPollSelectorImpl,委託給EPollArrayWrapper實現,其中三個native方法是對epoll的封裝,而EPollSelectorImpl. implRegister方法,通過調用epoll_ctl向epoll實例中註冊事件,還將註冊的文件描述符(fd)與SelectionKey的對應關係添加到fdToKey中,這個map維護了文件描述符與SelectionKey的映射。
fdToKey有時會變得非常大,因為註冊到Selector上的Channel非常多(百萬連接);過期或失效的Channel沒有及時關閉。fdToKey總是串行讀取的,而讀取是在select方法中進行的,該方法是非線程安全的。
Pipe:兩個線程之間的單向數據連接,數據會被寫到sink通道,從source通道讀取
NIO的服務端建立過程:Selector.open():打開一個Selector;ServerSocketChannel.open():創建服務端的Channel;bind():綁定到某個端口上。並配置非阻塞模式;register():註冊Channel和關注的事件到Selector上;select()輪詢拿到已經就緒的事件。
本文來自:奈學開發者社區,如有侵權,請聯繫我刪除~