1. 程式人生 > >什麼是阻塞式和非阻塞io流?

什麼是阻塞式和非阻塞io流?

Java中的阻塞和非阻塞IO包各自的優劣思考
NIO 設計背後的基石:反應器模式,用於事件多路分離和分派的體系結構模式。

反應器(Reactor):用於事件多路分離和分派的體系結構模式

通常的,對一個檔案描述符指定的檔案或裝置, 有兩種工作方式: 阻塞 與非阻塞 。所謂阻塞方式的意思是指, 當試圖對該檔案描述符進行讀寫時, 如果當時沒有東西可讀,或者暫時不可寫, 程式就進入等待 狀態, 直到有東西可讀或者可寫為止。而對於非阻塞狀態, 如果沒有東西可讀, 或者不可寫, 讀寫函式馬上返回, 而不會等待 。


一種常用做法是:每建立一個Socket連線時,同時建立一個新執行緒對該Socket進行單獨通訊(採用阻塞的方式通訊)。這種方式具有很高的響應速度,並且控制起來也很簡單,在連線數較少的時候非常有效,但是如果對每一個連線都產生一個執行緒的無疑是對系統資源的一種浪費,如果連線數較多將會出現資源不足的情況。

另一種較高效的做法是:伺服器端儲存一個Socket連線列表,然後對這個列表進行輪詢,如果發現某個Socket埠上有資料可讀時(讀就緒),則呼叫該socket連線的相應讀操作;如果發現某個 Socket埠上有資料可寫時(寫就緒),則呼叫該socket連線的相應寫操作;如果某個埠的Socket連線已經中斷,則呼叫相應的析構方法關閉該埠。這樣能充分利用伺服器資源,效率得到了很大提高。



傳統的阻塞式IO,每個連線必須要開一個執行緒來處理,並且沒處理完執行緒不能退出。

非阻塞式IO,由於基於反應器模式,用於事件多路分離和分派的體系結構模式,所以可以利用執行緒池來處理。事件來了就處理,處理完了就把執行緒歸還。而傳統阻塞方式不能使用執行緒池來處理,假設當前有10000個連線,非阻塞方式可能用1000個執行緒的執行緒池就搞定了,而傳統阻塞方式就需要開10000個來處理。如果連線數較多將會出現資源不足的情況。非阻塞的核心優勢就在這裡。

為什麼會這樣,下面就對他們做進一步細緻具體的分析:

首先,我們來分析傳統阻塞式IO的瓶頸在哪裡。在連線數不多的情況下,傳統IO編寫容易方便使用。但是隨著連線數的增多,問題傳統IO就不行了。因為前面說過,傳統IO處理每個連線都要消耗 一個執行緒,而程式的效率當執行緒數不多時是隨著執行緒數的增加而增加,但是到一定的數量之後,是隨著執行緒數的增加而減少。這裡我們得出結論,傳統阻塞式IO的瓶頸在於不能處理過多的連線。

然後,非阻塞式IO的出現的目的就是為了解決這個瓶頸。而非阻塞式IO是怎麼實現的呢?非阻塞IO處理連線的執行緒數和連線數沒有聯絡,也就是說處理10000個連線非阻塞IO不需要10000個執行緒,你可以用1000個也可以用2000個執行緒來處理。因為非阻塞IO處理連線是非同步的。當某個連線傳送請求到伺服器,伺服器把這個連線請求當作一個請求"事件",並把這個"事件"分配給相應的函式處理。我們可以把這個處理函式放到執行緒中去執行,執行完就把執行緒歸還。這樣一個執行緒就可以非同步的處理多個事件。而阻塞式IO的執行緒的大部分時間都浪費在等待請求上了。
[b]引用自:

http://javag.javaeye.com

阻塞式IO就是在進行讀寫的時候呼叫了某個方法,如read()或write()方法
在該方法執行完之前,會一直等待,直到該方法執行完畢。所謂阻塞式IO流,就是指在從資料流當中讀寫資料的的時候,阻塞當前執行緒,直到IO流可以重新使用為止,你也可以使用流的avaliableBytes()函式看看當前流當中有多少位元組可以讀取,這樣就不會再阻塞了。

Java code import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;

publicclass ChannelInputStream extends InputStream {

  
private ReadableByteChannel channel;

  
public ChannelInputStream(ReadableByteChannel channel) throws IllegalArgumentException {
    
if (channel ==null) {
      
thrownew IllegalArgumentException("The readable byte channel is null");
     }

    
this
.channel = channel;
   }

  
publicint read() throws IOException {
     ByteBuffer buffer
= ByteBuffer.allocate(1);
    
int result = channel.read(buffer);
    
if (result !=-1) {
       buffer.flip();
       result
= (int) buffer.get();
       buffer.clear();
     }
    
return result;
   }

  
publicint read(byte b[]) throws IOException {
     ByteBuffer buffer
= ByteBuffer.allocate(b.length);
    
int result = channel.read(buffer);
    
if (result !=-1) {
       buffer.flip();
       buffer.get(b,
0, result);
       buffer.clear();
     }
    
return result;
   }

  
publicint read(byte b[], int off, int len) throws IOException {
     ByteBuffer buffer
= ByteBuffer.allocate(b.length);
    
int result = channel.read(buffer);
    
if (result !=-1) {
       buffer.flip();
       buffer.get(b, off, len
> result ? result : len);
       buffer.clear();
     }
    
return result;
   }

  
publicvoid close() throws IOException {
     channel.close();
   }
}