IO的方式一般分爲幾種,同步阻塞的BIO、同步非阻塞的NIO、異步非阻塞的AIO。java
1、同步阻塞的BIOweb
在JDK1.4以前,咱們創建網絡鏈接的時候採用BIO模式,須要先在服務端啓動一個serverSocket,而後在客戶端啓動socket來對服務端進行通訊,默認狀況下服務端須要對每一個請求創建一堆線程等待請求,而客戶端發送請求後,先諮詢服務端是否有線程響應,若是沒有則會一直等待或者遭到拒絕請求,若是有的話,客戶端線程會等待請求結束後才繼續執行。編程
2、同步非阻塞的NIO後端
NIO自己是基於事件驅動思想來完成的,其主要想解決的是BIO的大併發問題,在使用同步I/O的網絡應用中,若是要同時處理多個客戶端請求,或是在客戶端要同時和多個服務器進行通信,就必須使用多線程來處理。也就是說,將每個客戶端請求分配給一個線程來單獨處理。這樣作雖然能夠達到咱們的要求,但同時又帶來另一個問題。因爲每建立一個線程,就要爲這個線程分配必定的內存空間,並且操做系統自己對線程的總數有必定的限制。若是客戶端的請求過多,服務端程序可能會由於不堪重負而拒絕客戶端的請求,甚至服務器可能會所以而癱瘓。服務器
NIO基於Reactor,當socket有流可讀或可寫入socket時,操做系統會相應的通知引用程序進行處理,應用再將流讀取到緩衝區或寫入操做系統。也就是說,這個時候,已經不是一個鏈接就要對應一個處理線程了,而是有效的請求,對應一個線程,當鏈接沒有數據時,是沒有工做線程來處理的。網絡
BIO和NIO一個比較重要的不一樣,是咱們使用BIO的時候每每會引入多線程,每一個鏈接一個單獨的線程;而NIO則是使用單線程或者使用少許線程,每一個鏈接公用一個線程。多線程
NIO的最重要的地方是當一個鏈接建立後,不須要對應一個線程,這個鏈接會被註冊到多路複用器上,因此全部的鏈接只須要一個線程就能搞定,當這個線程中多路複用器進行輪詢的時候,發現鏈接上有請求的話,纔開啓一個線程進行處理,也就是一個請求一個線程模式。架構
在NIO的處理方式中,當一個請求來的時候,開啓線程進行處理,可能會等待後端應用的資源(JDBC鏈接等),其實這個線程就被阻塞了,若是併發上來,還會有BIO同樣的問題。併發
HTTP/1.1出現後,有了Http長鏈接,這樣除了超時和指明特定關閉的http header外,這個連接是一直打開的狀態的,這樣在NIO處理中能夠進一步的進化,在後端資源中能夠實現資源池或者隊列,當請求來的話,開啓的線程把請求和請求數據傳送給後端資源池或者隊列裏面就返回,而且在全局的地方保持住這個現場(哪一個鏈接的哪一個請求等),這樣前面的線程仍是能夠去接受其餘的請求,然後端的應用的處理只須要執行隊列裏面的就能夠了,這樣請求處理和後端應用是異步的.當後端處理完,到全局地方獲得現場,產生響應,這個就實現了異步處理。異步
3、異步非阻塞AIO
與NIO不一樣,當進行讀寫操做時,只需直接調用API的read或write方法便可。這兩種方法均爲異步的,對於讀操做而言,當有流可讀取時,操做系統會將可讀的流傳入read方法的緩衝區,並通知應用程序;對於寫操做而言,當操做系統將write方法傳遞的流寫入完畢時,操做系統主動通知應用程序。便可以理解爲, read/write方法都是異步的,完成後會主動調用回調函數。 在JDK1.7中,這部份內容成爲AIO,
主要在java.nio.channels包下增長了下面四個異步通道:
AsynchronousSocketChannel
AsynchronousServerSocketChannel
AsynchronousFileChannel
AsynchronousDatagramChannel
其中的read/write方法,會返回一個帶回調函數的對象,當執行完讀取/寫入操做後,直接調用回調函數。
BIO是一個鏈接一個線程。
NIO是一個請求一個線程。
AIO是一個有效請求一個線程。
先來個例子理解一下概念,以銀行取款爲例:
同步 : 本身親自出馬持銀行卡到銀行取錢(使用同步IO時,Java本身處理IO讀寫);
異步 : 委託一小弟拿銀行卡到銀行取錢,而後給你(使用異步IO時,Java將IO讀寫委託給OS處理,須要將數據緩衝區地址和大小傳給OS(銀行卡和密碼),OS須要支持異步IO操做API);
阻塞 : ATM排隊取款,你只能等待(使用阻塞IO時,Java調用會一直阻塞到讀寫完成才返回);
非阻塞 : 櫃檯取款,取個號,而後坐在椅子上作其它事,等號廣播會通知你辦理,沒到號你就不能去,你能夠不斷問大堂經理排到了沒有,大堂經理若是說還沒到你就不能去(使用非阻塞IO時,若是不能讀寫Java調用會立刻返回,當IO事件分發器會通知可讀寫時再繼續進行讀寫,不斷循環直到讀寫完成)
4、java對BIO、NIO、AIO的支持
java BIO:同步並阻塞
在此種方式下,用戶進程在發起一個IO操做之後,必須等待IO操做的完成,只有當真正完成了IO操做之後,用戶進程才能運行。JAVA傳統的IO模型屬於此種方式!
服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。
java NIO:同步非阻塞
在此種方式下,用戶進程發起一個IO操做之後邊可返回作其它事情,可是用戶進程須要時不時的詢問IO操做是否就緒,這就要求用戶進程不停的去詢問,從而引入沒必要要的CPU資源浪費。其中目前JAVA的NIO就屬於同步非阻塞IO。
服務器實現模式爲一個請求一個線程,即客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有I/O請求時才啓動一個線程進行處理。
java AIO:異步非阻塞
在此種模式下,用戶進程只須要發起一個IO操做而後當即返回,等IO操做真正的完成之後,應用程序會獲得IO操做完成的通知,此時用戶進程只須要對數據進行處理就行了,不須要進行實際的IO讀寫操做,由於真正的IO讀取或者寫入操做已經由內核完成了。目前Java中尚未支持此種IO模型。
服務器實現模式爲一個有效請求一個線程,客戶端的I/O請求都是有OS先完成了再通知服務器應用去啓動線程進行處理。
5、BIO、NIO、AIO適用場景分析
BIO方式適用於鏈接數目比較少且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4之前的惟一選擇,程序只管簡單易理解。
NIO方式適用於鏈接數目多且比較短的架構,好比聊天服務器,併發侷限於應用中,編程比較複雜,JDK1.4開始支持。
AIO方式適用於鏈接數目多且鏈接比較長的架構,好比相冊服務器,充分調用OS參與併發操做,編程比較複雜,JDK1.7開始支持。
6、 Tomcat( BIO )和Jetty(NIO)
Tomcat和Jetty是目前全球範圍內最著名的兩款開源的webserver/servlet容器。
相同點:
一、Tomcat和Jetty都是一種servlet引擎,他們都支持標準的servlet規範和JavaEE的規範。
不一樣點:
一、架構比較
(1)Jetty的架構比Tomcat簡單。
(2)Jetty的架構是基於handler來實現的,主要的擴展功能均可以使用handler來實現,擴展簡單。
(3)Tomcat的架構是基於容器設計的,進行擴展是須要了解Tomcat的總體設計結構,不易擴展。
二、性能比較
(1)Jetty和Tomcat性能方面差別不大。
(2)Jetty能夠同時處理大量鏈接並且能夠長時間保持鏈接,適合web聊天應用等等。
(3)Jetty的架構簡單,所以做爲服務器,Jetty能夠按需加載組件,減小沒必要要的組件,減小了服務器內部開銷,從而提升服務器性能。
(4)Jetty默認採用NIO結束處理I/O請求上更佔優點,在處理靜態資源時,性能較高。
三、Tomcat適合處理少數很是繁忙的連接,Tomcat的整體性能更高。Tomcat默認採用BIO處理I/O請求,在處理靜態資源時,性能較差。
四、其餘比較
(1)Jetty的應用更加快速,修改簡單,對新的servlet規範的支持較好。
(2)Tomcat目前應用比較普遍,對javaEE和servlet的支持更加全面,不少性能會直接集成進來。
支持博主,請點贊轉發收藏,私信「資料」獲取更多資料