阻塞式和非阻塞式IO


有很多人把阻塞認為是同步,把非阻塞認為是異步;個人認為這樣是不准確的,當然從思想上可以這樣類比,但方式是完全不同的,下面說說在JAVA里面阻塞IO和非阻塞IO的區別

     在JDK1.4中引入了一個NIO的類庫,使得Java涉及IO的操作擁有阻塞式和非阻塞式兩種,問一下阻塞IO與非阻塞IO有什么區別?有什么優缺點?

在阻塞模式下,若從網絡流中讀取不到指定大小的數據量,阻塞IO就在那里阻塞着。比如,已知后面會有10個字節的數據發過來,但是我現在只收到8個字節,那么當前線程就在那傻傻地等到下一個字節的到來,對,就在那等着,啥事也不做,直到把這10個字節讀取完,這才將阻塞放開通行。

在非阻塞模式下,若從網絡流中讀取不到指定大小的數據量,非阻塞IO就立即通行。比如,已知后面會有10個字節的數據發過來,但是我現在只收到8個字節,那么當前線程就讀取這8個字節的數據,讀完后就立即返回,等另外兩個字節再來的時候再去讀取。

從上面可以看出,阻塞IO在性能方面是很低下的,如果要使用阻塞IO完成一個Web服務器的話,那么對於每一個請求都必須啟用一個線程進行處理。而使用非阻塞IO的話,一到兩個線程基本上就夠了,因為線程不會產生阻塞,好比一下接收A請求的數據,另一下接收B請求的數據,等等,就是不停地東奔西跑,直接到把數據接收完了。

雖然說,非阻塞IO比阻塞IO有更高的性能,但是對於開發來的,難度就成數倍遞增了。由於是有多少數據就讀取多少數據,這樣在讀取完整之前需要將已經讀取到的數據保存起來,而且需要與其他地方來的數據隔離開來不能混在一起,否則就不知道這數據是誰的了,呵呵。

 

我們都知道TCP是面向連接的傳輸層協議,一個socket必定會有綁定一個連接,在普通的BIO(阻塞式IO)中,需要有三次握手,然后一般的socket編程就是這樣的形式。

Socket服務器端流程如下:加載套接字->創建監聽的套接字->綁定套接字->監聽套接字->處理客戶端相關請求。

Socket客戶端同樣需要先加載套接字,然后創建套接字,不過之后不用綁定和監聽了,而是直接連接服務器,發送相關請求。

 

 

 

       他們一直就占用這個連接,如果有信息發送,那么就響應,否則就一直阻塞着。如果有多連接,那么就要使用多線程,一個線程處理一個連接,在連接還少的情況下,是允許的,但如果同時處理的連接過多比如說1000,那么在win平台上就會遇到瓶頸了如果2000,那么在linux上就遇到瓶頸了,因為在不同的平台上每一個進程能夠創建的線程數是有限度的,並且過多的線程必將會引起系統對線程調度的效率問題,再怎么也要保證線程優先隊列,阻塞隊列;假設一千個線程,一個線程最少一兆的棧大小,對內存也是一個很大的消耗。

 

       總之阻塞式的IO是:一連接<一一一>一線程 

 

       然后出現了NIO,在java1.4引入了java.nio包,java new I/O。引入了操作系統中常用的緩沖區和通道等概念。

 

       緩沖區: 在操作系統中緩沖區是為了解決CPU的計算速度和外設輸入輸出速度不匹配的問題,因為外設太慢了,如果沒有緩沖區,那么CPU在外設輸入的時候就要一直等着,就會造成CPU處理效率的低下,引入了緩沖之后,外設直接把數據放到緩沖中,當數據傳輸完成之后,給CPU一個中斷信號,通知CPU:“我的數據傳完了,你自己從緩沖里面去取吧”。如果是輸出也是一樣的道理。

 

       通道: 那么通道用來做什么呢?其實從他的名字就可以看出,它就是一條通道,您想傳遞出去的數據被放置在緩沖區中,然后緩沖區中怎么從哪里傳輸出去呢?或者外設怎么把數據傳輸到緩沖中呢?這里就要用到通道。它可以進一步的減少CPU的干預,同時更有效率的提高整個系統的資源利用率,例如當CPU要完成一組相關的讀操作時,只需要向I/O通道發送一條指令,以給出其要執行的通道程序的首地址和要訪問的設備,通道執行通道程序便可以完成CPU指定的I/O任務。

 

      選擇器: 另外一項創新是選擇器,當我們使用通道的時候也許通道沒有准備好,或者有了新的請求過來,或者線程遇到了阻塞,而選擇器恰恰可以幫助CPU了解到這些信息,但前提是將這個通道注冊到了這個選擇器。

下面一個例子是我看過的一個講述的很貼切的例子:

一輛從 A 開往 B 的公共汽車上,路上有很多點可能會有人下車。司機不知道哪些點會有哪些人會下車,對於需要下車的人,如何處理更好?

1. 司機過程中定時詢問每個乘客是否到達目的地,若有人說到了,那么司機停車,乘客下車。 ( 類似阻塞式 )

2. 每個人告訴售票員自己的目的地,然后睡覺,司機只和售票員交互,到了某個點由售票員通知乘客下車。 ( 類似非阻塞 )

很顯然,每個人要到達某個目的地可以認為是一個線程,司機可以認為是 CPU 。在阻塞式里面,每個線程需要不斷的輪詢,上下文切換,
以達到找到目的地的結果。而在非阻塞方式里,每個乘客 ( 線程 ) 都在睡覺 ( 休眠 ) ,只在真正外部環境准備好了才喚醒,這樣的喚醒肯定不會阻塞。

 

 


免責聲明!

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



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