Java中有兩套IO模型,分別是傳統IO和JDK1.4新引入的NIO(叫New IO或 Non-Blocking IO,後者更能體現它的設計理念)。
在傳統IO模型中,網絡IO是基於數據流(Stream)的,數據的輸入和輸出分別對應InputStream和OutputStream。流數據只能逐字節的讀取(或寫入),直到全部數據讀取(或寫入)完畢,流數據只能單向的讀取(或寫入),無緩存區,沒法對數據進行先後移動訪問。
在NIO模型中,網絡IO是基於Channel和IO多路複用技術,實現原理更接近OS的IO模型,網絡讀IO的兩階段模型(數據準備、數據拷貝)中,數據準備階段是Non-Blocking的,Channel和Buffer配合主要處理的是數據拷貝階段的工做。緩存
傳統IO模式,在處理數據流過程當中,線程處於阻塞狀態,直到流處理(讀取後寫入)完畢,因此一個線程只能處理一個IO任務,若是IO未準備就緒無數據(或不可寫)線程只能一直等待,直到能夠有數據(或可寫);
NIO模式,採用IO多路複用技術,使用選擇器(Selector)監控一組IO,雖然Selector線程也處於阻塞狀態,但一個線程能夠同時處理多個IO任務,當IO就緒時,Selector返回就緒的IO並由程序進行處理。服務器
如上圖所示,NIO的核心涉及3部分,分別爲Selector、Channel和Buffer。在描述數據讀寫操做時,須要特別注意讀寫的主體,從Channel的角度,讀數據是指把數據從Channel讀取數據寫入Buffer,寫數據是指把Buffer的數據寫入Channel;從Buffer的角度,寫數據是指把數據寫入Buffer,讀數據是指從Buffer中讀取數據寫入Channel或轉換爲其餘數據類型。網絡
Selector是NIO多理複用的執行組件,系統在處理多個IO任務時,能夠把每一個IO任務以Channel的方式註冊到Selector,經過Selector.select()統一監控向其註冊的Channel的狀態,select()方法會阻塞,直到其中有Channel的IO狀態準備就緒有數據可讀(或IO可寫)時,select()返回就緒IO的Channel由程序處理相關事件。
Selector只能監聽SelectableChannel類型的Channel,主要應用場景爲網絡IO等數據準備階段時間較長的IO,SocketChannel屬於此類型,而FileChannel不屬於。app
Selector的主要方法:post
Channel是NIO中數據處理的入口,每一個Channel對應着一個IO的底層數據Buffer,負責對Buffer的寫入或讀取操做。根據IO類型不一樣,Java中提供了以下通用Channel實現。this
SocketChannel的主要方法:spa
register():向Selector註冊Channel感興趣的事件,事件包括:線程
當Socket關閉後,會觸發對端的OP_READ事件,但此時read()返回-1或throw IOexception,能夠經過這兩個判斷來肯定對端是否關閉,並調用close()方法關閉SocketChannel並執行清理工做。
Buffer本質上是一塊連續的內存區域,提供數據緩存功能和Channel對數據的讀寫操做能力。NIO提供了 ByteBuffer / CharBuffer / DoubleBuffer / FloatBuffer / IntBuffer / LongBuffer / ShortBuffer
Buffer類型,實現讀Java基本類型的緩存支持。對Buffer的讀寫操做過程當中,涉及3個關鍵參數,即postion、limit和capacity。其中position和limit在讀/寫操做時具備不一樣的意義,而capacity讀寫時都是表示Buffer的大小。Buffer初始處於寫狀態,數據寫入後,經過flip切換的讀取狀態,讀取完後,再經過clear清除數據,準備寫入。設計
Capacity
Buffer是一個固定大小的內存緩存區,Capacity就表示其總長度。向Buffer寫入的數據字節(byte/char/int/long等)總長度不能超過Capacity。當Buffer寫滿時,須要(讀取)清空後才能繼續寫入。code
Position
Position初始位置爲0,當Buffer讀寫模式切換時,也會自動重置爲0。
讀取模式時,Position表示當前讀取數據的位置,讀取當前位置數據後,Position自動移到下一個可讀位置,讀取位置不能超過Limit。
寫入模式時,Position表達當前寫入數據的位置,在當前位置寫入數據後,Position自動移到下一個寫入位置,寫入位置不能超過Capacity - 1。
Limit
讀模式時,Limit表示Buffer中可讀取數據長度,當Buffer從寫模式切換到讀模式時,Limit值等於寫模式時的Position。
寫模式時,Limit表示可寫入數據總長度,其值等於Capacity - 1。
Buffer的主要方法:
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; } public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; } public final Buffer rewind() { position = 0; mark = -1; return this; }