【Java】Java NIO
NIO
為什麽要使用 NIO?
NIO 的創建目的是為了讓 Java 程序員可以實現高速 I/O 而無需編寫自定義的本機代碼。NIO 將最耗時的 I/O 操作(即填充和提取緩沖區)轉移回操作系統,因而可以極大地提高速度。
流與塊的比較
原來的 I/O 庫(在 java.io.*中) 與 NIO 最重要的區別是數據打包和傳輸的方式。正如前面提到的,原來的 I/O 以流的方式處理數據,而 NIO 以塊的方式處理數據。
面向流 的 I/O 系統一次一個字節地處理數據。一個輸入流產生一個字節的數據,一個輸出流消費一個字節的數據。為流式數據創建過濾器非常容易。鏈接幾個過濾器,以便每個過濾器只負責單個復雜處理機制的一部分,這樣也是相對簡單的。不利的一面是,面向流的 I/O 通常相當慢。
NIO的buffer機制
NIO性能的優勢就來源於緩沖的機制,不管是讀或者寫都需要以塊的形式寫入到緩沖區中。NIO實際上讓我們對IO的操作更接近於操作系統的實際過程。
所有的系統I/O都分為兩個階段:等待就緒和操作。舉例來說,讀函數,分為等待系統可讀和真正的讀;同理,寫函數分為等待網卡可以寫和真正的寫。
以socket為例:
先從應用層獲取數據到內核的緩沖區,然後再從內核的緩沖區復制到進程的緩沖區。所以實際上底層的機制也是不斷利用緩沖區來讀寫數據的。即使傳統IO抽象成了從流直接讀取數據,但本質上也依然是利用緩沖區來讀取和寫入數據。
NIO的非阻塞機制
NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什麽都不會獲取。而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。 線程通常將非阻塞IO的空閑時間用於在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。
下圖是幾種常見I/O模型的對比:
以socket.read()為例子:
傳統的BIO裏面socket.read(),如果TCP RecvBuffer裏沒有數據,函數會一直阻塞,直到收到數據,返回讀到的數據。
對於NIO,如果TCP RecvBuffer有數據,就把數據從網卡讀到內存,並且返回給用戶;反之則直接返回0,永遠不會阻塞。所以我們可以NIO實現同時監聽多個IO通道,然後不斷的輪詢尋找可以讀寫的設備。
NIO的IO模型可以理解為是IO多路復用模型和非阻塞模型,同時還有事件驅動模型。
這裏需要知道一點,就是IO多路復用是一定需要實現非阻塞的。
小結
NIO相對於IO流的優勢:
- 非阻塞
- buffer機制
- 流替代塊
參考:
- https://tech.meituan.com/nio.html
- http://www.importnew.com/19816.html
- https://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html
【Java】Java NIO