BIO、NIO、AIO 介紹和適用場景分析(絕對經典)

IO的方式通常分爲幾種,同步阻塞的BIO、同步非阻塞的NIO、異步非阻塞的AIO。

一、同步阻塞的BIO

在JDK1.4之前,我們建立網絡連接的時候採用BIO模式,需要先在服務端啓動一個serverSocket,然後在客戶端啓動socket來對服務端進行通信,默認情況下服務端需要對每個請求建立一堆線程等待請求,而客戶端發送請求後,先諮詢服務端是否有線程相應,如果沒有則會一直等待或者遭到拒絕請求,如果有的話,客戶端線程會等待請求結束後才繼續執行。

二、同步非阻塞的NIO

NIO本身是基於事件驅動思想來完成的,其主要想解決的是BIO的大併發問題,在使用同步I/O的網絡應用中,如果要同時處理多個客戶端請求,或是在客戶端要同時和多個服務器進行通訊,就必須使用多線程來處理。也就是說,將每一個客戶端請求分配給一個線程來單獨處理。這樣做雖然可以達到我們的要求,但同時又帶來另外一個問題。由於每創建一個線程,就要爲這個線程分配一定的內存空間,而且操作系統本身對線程的總數有一定的限制。如果客戶端的請求過多,服務端程序可能會因爲不堪重負而拒絕客戶端的請求,甚至服務器可能會因此而癱瘓。

NIO基於Reactor,當socket有流可讀或可寫入socket時,操作系統會相應的通知引用程序進行處理,應用再將流讀取到緩衝區或寫入操作系統。也就是說,這個時候,已經不是一個連接就要對應一個處理線程了,而是有效的請求,對應一個線程,當連接沒有數據時,是沒有工作線程來處理的。

BIO和NIO一個比較重要的不同,是我們使用BIO的時候往往會引入多線程,每個連接一個單獨的線程;而NIO則是使用單線程或者使用少量線程,每個連接公用一個線程。

 

三、異步非阻塞AIO 

 

1、異步非阻塞AIO 

與NIO不同,當進行讀寫操作時,只需直接調用API的read或write方法即可。這兩種方法均爲異步的,對於讀操作而言,當有流可讀取時,操作系統會將可讀的流傳入read方法的緩衝區,並通知應用程序;對於寫操作而言,當操作系統將write方法傳遞的流寫入完畢時,操作系統主動通知應用程序。即可以理解爲, read/write方法都是異步的,完成後會主動調用回調函數。   在JDK1.7中,這部分內容成爲AIO。

2、主要在java.nio.channels包下增加了下面四個異步通道

  • AsynchronousSocketChannel
  • AsynchronousServerSocketChannel
  • AsynchronousFileChannel
  • AsynchronousDatagramChannel

其中的read/write方法,會返回一個帶回調函數的對象,當執行完讀取/寫入操作後,直接調用回調函數。  

BIO是一個連接一個線程。

NIO是一個請求一個線程。

AIO是一個有效請求一個線程。

3、以銀行取款爲例,理解一下概念

  • 同步 : 自己親自出馬持銀行卡到銀行取錢(使用同步IO時,Java自己處理IO讀寫);
  • 異步 : 委託一小弟拿銀行卡到銀行取錢,然後給你(使用異步IO時,Java將IO讀寫委託給OS處理,需要將數據緩衝區地址和大小傳給OS(銀行卡和密碼),OS需要支持異步IO操作API);
  • 阻塞 : ATM排隊取款,你只能等待(使用阻塞IO時,Java調用會一直阻塞到讀寫完成才返回);
  • 非阻塞 : 櫃檯取款,取個號,然後坐在椅子上做其它事,等號廣播會通知你辦理,沒到號你就不能去,你可以不斷問大堂經理排到了沒有,大堂經理如果說還沒到你就不能去(使用非阻塞IO時,如果不能讀寫Java調用會馬上返回,當IO事件分發器會通知可讀寫時再繼續進行讀寫,不斷循環直到讀寫完成)

四、java對BIO、NIO、AIO的支持

1、java BIO:同步並阻塞

在此種方式下,用戶進程在發起一個IO操作以後,必須等待IO操作的完成,只有當真正完成了IO操作以後,用戶進程才能運行。JAVA傳統的IO模型屬於此種方式!

服務器實現模式爲一個連接一個線程,即客戶端有連接請求時服務器端需要啓動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。

2、java NIO:同步非阻塞

在此種方式下,用戶進程發起一個IO操作以後邊可返回做其它事情,但是用戶進程需要時不時的詢問IO操作是否就緒,這就要求用戶進程不停的去詢問,從而引入不必要的CPU資源浪費。其中目前JAVA的NIO就屬於同步非阻塞IO。

服務器實現模式爲一個請求一個線程,即客戶端發送的連接請求都會註冊到多路複用器上,多路複用器輪詢到連接有I/O請求時才啓動一個線程進行處理。

3、java AIO:異步非阻塞

在此種模式下,用戶進程只需要發起一個IO操作然後立即返回,等IO操作真正的完成以後,應用程序會得到IO操作完成的通知,此時用戶進程只需要對數據進行處理就好了,不需要進行實際的IO讀寫操作,因爲真正的IO讀取或者寫入操作已經由內核完成了。目前Java中還沒有支持此種IO模型。 

服務器實現模式爲一個有效請求一個線程,客戶端的I/O請求都是有OS先完成了再通知服務器應用去啓動線程進行處理。

五、BIO、NIO、AIO適用場景分析

BIO方式適用於連接數目比較少且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,程序只管簡單易理解。

NIO方式適用於連接數目多且比較短的架構,比如聊天服務器,併發侷限於應用中,編程比較複雜,JDK1.4開始支持。

AIO方式適用於連接數目多且連接比較長的架構,比如相冊服務器,充分調用OS參與併發操作,編程比較複雜,JDK1.7開始支持。

六、 Tomcat( BIO )和Jetty(NIO)

Tomcat和Jetty是目前全球範圍內最著名的兩款開源的webserver/servlet容器。

相同點:

1、Tomcat和Jetty都是一種servlet引擎,他們都支持標準的servlet規範和JavaEE的規範。

不同點:

1、架構比較

(1)Jetty的架構比Tomcat簡單。

(2)Jetty的架構是基於handler來實現的,主要的擴展功能都可以使用handler來實現,擴展簡單。

(3)Tomcat的架構是基於容器設計的,進行擴展是需要了解Tomcat的整體設計結構,不易擴展。

2、性能比較

(1)Jetty和Tomcat性能方面差異不大。

(2)Jetty可以同時處理大量連接而且可以長時間保持連接,適合web聊天應用等等。

(3)Jetty的架構簡單,因此作爲服務器,Jetty可以按需加載組件,減少不必要的組件,減少了服務器內部開銷,從而提高服務器性能。

(4)Jetty默認採用NIO結束處理I/O請求上更佔優勢,在處理靜態資源時,性能較高。

(5)Tomcat適合處理少數非常繁忙的鏈接,Tomcat的總體性能更高。Tomcat默認採用BIO處理I/O請求,在處理靜態資源時,性能較差。

3、其他比較

(1)Jetty的應用更加快速,修改簡單,對新的servlet規範的支持較好。

(2)Tomcat目前應用比較廣泛,對javaEE和servlet的支持更加全面,很多性能會直接集成進來。

 

上一篇:Java泛型基礎知識總結(超級詳細)

下一篇:深入理解Java枚舉類型 + EnumMap源碼分析

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章