1. 程式人生 > >【Java】Java NIO

【Java】Java NIO

負責 無需 阻塞 數據 images 讀寫 imageview 底層 所有

NIO

為什麽要使用 NIO?

NIO 的創建目的是為了讓 Java 程序員可以實現高速 I/O 而無需編寫自定義的本機代碼。NIO 將最耗時的 I/O 操作(即填充和提取緩沖區)轉移回操作系統,因而可以極大地提高速度。

流與塊的比較

原來的 I/O 庫(在 java.io.*中) 與 NIO 最重要的區別是數據打包和傳輸的方式。正如前面提到的,原來的 I/O 以流的方式處理數據,而 NIO 以塊的方式處理數據。
面向流 的 I/O 系統一次一個字節地處理數據。一個輸入流產生一個字節的數據,一個輸出流消費一個字節的數據。為流式數據創建過濾器非常容易。鏈接幾個過濾器,以便每個過濾器只負責單個復雜處理機制的一部分,這樣也是相對簡單的。不利的一面是,面向流的 I/O 通常相當慢。

一個 面向塊 的 I/O 系統以塊的形式處理數據。每一個操作都在一步中產生或者消費一個數據塊。按塊處理數據比按(流式的)字節處理數據要快得多。但是面向塊的 I/O 缺少一些面向流的 I/O 所具有的優雅性和簡單性。

NIO的buffer機制

NIO性能的優勢就來源於緩沖的機制,不管是讀或者寫都需要以塊的形式寫入到緩沖區中。NIO實際上讓我們對IO的操作更接近於操作系統的實際過程。
所有的系統I/O都分為兩個階段:等待就緒和操作。舉例來說,讀函數,分為等待系統可讀和真正的讀;同理,寫函數分為等待網卡可以寫和真正的寫。
以socket為例:
先從應用層獲取數據到內核的緩沖區,然後再從內核的緩沖區復制到進程的緩沖區。所以實際上底層的機制也是不斷利用緩沖區來讀寫數據的。即使傳統IO抽象成了從流直接讀取數據,但本質上也依然是利用緩沖區來讀取和寫入數據。

所以,為了更好的理解nio,我們就需要知道IO的底層機制,這樣對我們將來理解channel和buffer就打下了基礎。這裏簡單提一下,我們可以把bufffer就理解為內核緩沖區,所以不論讀寫,自然都要經過這個區域,讀的話,先從設備讀取數據到內核,再讀到進程緩沖區,寫的話,先從進程緩沖區寫到內核,再從內核寫回設備。

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