Java高效NIO之Java IO基礎

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並由程序進行處理。服務器

Stream IO vs NIO.PNG

NIO核心設計

如上圖所示,NIO的核心涉及3部分,分別爲Selector、Channel和Buffer。在描述數據讀寫操做時,須要特別注意讀寫的主體,從Channel的角度,讀數據是指把數據從Channel讀取數據寫入Buffer,寫數據是指把Buffer的數據寫入Channel;從Buffer的角度,寫數據是指把數據寫入Buffer,讀數據是指從Buffer中讀取數據寫入Channel或轉換爲其餘數據類型。網絡

Selector

Selector是NIO多理複用的執行組件,系統在處理多個IO任務時,能夠把每一個IO任務以Channel的方式註冊到Selector,經過Selector.select()統一監控向其註冊的Channel的狀態,select()方法會阻塞,直到其中有Channel的IO狀態準備就緒有數據可讀(或IO可寫)時,select()返回就緒IO的Channel由程序處理相關事件。
Selector只能監聽SelectableChannel類型的Channel,主要應用場景爲網絡IO等數據準備階段時間較長的IO,SocketChannel屬於此類型,而FileChannel不屬於。app

Channel繼承關係.PNG

Selector的主要方法:post

  1. open():靜態方法,建立Selector對象
  2. select():查詢IO準備就緒的Channel

Channel

Channel是NIO中數據處理的入口,每一個Channel對應着一個IO的底層數據Buffer,負責對Buffer的寫入或讀取操做。根據IO類型不一樣,Java中提供了以下通用Channel實現。this

  1. FileChannel:文件讀寫操做
  2. SocketChannel:TCP Socket鏈接
  3. ServerSocketChannel:TCP Socket服務器端,處理監聽端口創建Socket的accept事件
  4. DatagramChannel:UDP鏈接

SocketChannel的主要方法:spa

  1. register():向Selector註冊Channel感興趣的事件,事件包括:線程

    1. OP_ACCEPT:ServerSocketChannel收到客戶端鏈接事件
    2. OP_CONNECT:客戶端鏈接服務器成功事件
    3. OP_READ:Channel收到數據,可讀事件
    4. OP_WRITE:Channel可寫事件
  2. open():靜態方法,創建Socket Channel通道
  3. read():從Channel中讀取數據,寫入Buffer
  4. write():把Buffer中數據寫入Channel
  5. map():把Channel中部分數據或者所有數據映射成MappedByteBuffer
當Socket關閉後,會觸發對端的OP_READ事件,但此時read()返回-1或throw IOexception,能夠經過這兩個判斷來肯定對端是否關閉,並調用close()方法關閉SocketChannel並執行清理工做。

Buffer

Buffer本質上是一塊連續的內存區域,提供數據緩存功能和Channel對數據的讀寫操做能力。NIO提供了 ByteBuffer / CharBuffer / DoubleBuffer / FloatBuffer / IntBuffer / LongBuffer / ShortBuffer Buffer類型,實現讀Java基本類型的緩存支持。對Buffer的讀寫操做過程當中,涉及3個關鍵參數,即postion、limit和capacity。其中position和limit在讀/寫操做時具備不一樣的意義,而capacity讀寫時都是表示Buffer的大小。Buffer初始處於寫狀態,數據寫入後,經過flip切換的讀取狀態,讀取完後,再經過clear清除數據,準備寫入。設計

Buffer.PNG

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的主要方法:

  1. allocate():靜態方法,分配Buffer空間
  2. allocateDirect():靜態方法,分配Native堆Buffer空間
  3. flip():調整position和limit到讀取數據狀態
  4. clear():清空數據,爲再次寫入數據準備
  5. rewind():設置position爲0,重置讀取位置
  6. put()/get():寫入和讀取數據
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;
}
相關文章
相關標籤/搜索