Java NIO:IO與NIO的區別 -阿里


 一、概念

     NIO即New IO,這個庫是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但實現方式不同,NIO主要用到的是塊,所以NIO的效率要比IO高很多。在Java API中提供了兩套NIO,一套是針對標准輸入輸出NIO,另一套就是網絡編程NIO。

二、NIO和IO的主要區別

下表總結了Java IO和NIO之間的主要區別:

IO NIO
面向流 面向緩沖
阻塞IO 非阻塞IO
選擇器

 

 

 

 

1、面向流與面向緩沖

     Java IO和NIO之間第一個最大的區別是,IO是面向流的,NIO是面向緩沖區的。 Java IO面向流意味着每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何地方。此外,它不能前后移動流中的數據。如果需要前后移動從流中讀取的數據,需要先將它緩存到一個緩沖區。 Java NIO的緩沖導向方法略有不同。數據讀取到一個它稍后處理的緩沖區,需要時可在緩沖區中前后移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩沖區時,不要覆蓋緩沖區里尚未處理的數據。

2、阻塞與非阻塞IO

     Java IO的各種流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再干任何事情了。Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什么都不會獲取,而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。 線程通常將非阻塞IO的空閑時間用於在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。 

 io的各種流是阻塞的,就是當一個線程調用讀寫方法時,該線程會被阻塞,直到讀寫完,在這期間該線程不能干其他事,CPU轉而去處理其他線程,假如一個線程監聽一個端口,一天只會有幾次請求進來,但是CPU卻不得不為該線程不斷的做上下文切換,並且大部分切換以阻塞告終。

 NIO通訊是將整個任務切換成許多小任務,由一個線程負責處理所有io事件,並負責分發。它是利用事件驅動機制,而不是監聽機制,事件到的時候再觸發。NIO線程之間通過wait,notify等方式通訊。保證了每次上下文切換都有意義,減少無謂的進程切換。 

3、選擇器(Selectors)

     Java NIO的選擇器允許一個單獨的線程來監視多個輸入通道,你可以注冊多個通道使用一個選擇器,然后使用一個單獨的線程來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已准備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。

三、NIO和IO如何影響應用程序的設計

無論您選擇IO或NIO工具箱,可能會影響您應用程序設計的以下幾個方面:

1.對NIO或IO類的API調用。
2.數據處理。
3.用來處理數據的線程數。

1、API調用

當然,使用NIO的API調用時看起來與使用IO時有所不同,但這並不意外,因為並不是僅從一個InputStream逐字節讀取,而是數據必須先讀入緩沖區再處理。

2、數據處理

使用純粹的NIO設計相較IO設計,數據處理也受到影響。

在IO設計中,我們從InputStream或 Reader逐字節讀取數據。假設你正在處理一基於行的文本數據流,例如:

Name: Anna 
Age: 25
Email: anna@mailserver.com 
Phone: 1234567890 

該文本行的流可以這樣處理: 

InputStream input = ... ; // get the InputStream from the client socket   

BufferedReader reader = new BufferedReader(new InputStreamReader(input));   

String nameLine   = reader.readLine(); 
String ageLine    = reader.readLine(); 
String emailLine  = reader.readLine(); 
String phoneLine  = reader.readLine(); 

 

     請注意處理狀態由程序執行多久決定。換句話說,一旦reader.readLine()方法返回,你就知道肯定文本行就已讀完, readline()阻塞直到整行讀完,這就是原因。你也知道此行包含名稱;同樣,第二個readline()調用返回的時候,你知道這行包含年齡等。 正如你可以看到,該處理程序僅在有新數據讀入時運行,並知道每步的數據是什么。一旦正在運行的線程已處理過讀入的某些數據,該線程不會再回退數據(大多如此)。下圖也說明了這條原則:

而一個NIO的實現會有所不同,下面是一個簡單的例子:

ByteBuffer buffer = ByteBuffer.allocate(48); 
int bytesRead = inChannel.read(buffer); 

注意第二行,從通道讀取字節到ByteBuffer。當這個方法調用返回時,你不知道你所需的所有數據是否在緩沖區內。你所知道的是,該緩沖區包含一些字節,這使得處理有點困難。假設第一次 read(buffer)調用后,讀入緩沖區的數據只有半行,例如,“Name:An”,你能處理數據嗎?顯然不能,需要等待,直到整行數據讀入緩存,在此之前,對數據的任何處理毫無意義。所以,你怎么知道是否該緩沖區包含足夠的數據可以處理呢?好了,你不知道。發現的方法只能查看緩沖區中的數據。其結果是,在你知道所有數據都在緩沖區里之前,你必須檢查幾次緩沖區的數據。這不僅效率低下,而且可以使程序設計方案雜亂不堪。例如: 

ByteBuffer buffer = ByteBuffer.allocate(48);   

int bytesRead = inChannel.read(buffer);   

while(! bufferFull(bytesRead) ) {   
       bytesRead = inChannel.read(buffer);   
} 

 

bufferFull()方法必須跟蹤有多少數據讀入緩沖區,並返回真或假,這取決於緩沖區是否已滿。換句話說,如果緩沖區准備好被處理,那么表示緩沖區滿了。

bufferFull()方法掃描緩沖區,但必須保持在bufferFull()方法被調用之前狀態相同。如果沒有,下一個讀入緩沖區的數據可能無法讀到正確的位置。這是不可能的,但卻是需要注意的又一問題。

如果緩沖區已滿,它可以被處理。如果它不滿,並且在你的實際案例中有意義,你或許能處理其中的部分數據。但是許多情況下並非如此。下圖展示了“緩沖區數據循環就緒”:

四、總結

NIO可讓您只使用一個(或幾個)單線程管理多個通道(網絡連接或文件),但付出的代價是解析數據可能會比從一個阻塞流中讀取數據更復雜。

如果需要管理同時打開的成千上萬個連接,這些連接每次只是發送少量的數據,例如聊天服務器,實現NIO的服務器可能是一個優勢。同樣,如果你需要維持許多打開的連接到其他計算機上,如P2P網絡中,使用一個單獨的線程來管理你所有出站連接,可能是一個優勢。一個線程多個連接的設計方案如下圖所示:

Java NIO: 單線程管理多個連接

如果你有少量的連接使用非常高的帶寬,一次發送大量的數據,也許典型的IO服務器實現可能非常契合。下圖說明了一個典型的IO服務器設計:

Java IO: 一個典型的IO服務器設計- 一個連接通過一個線程處理. 

參考:Java NIO:IO與NIO的區別


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM