Java的IO:BIO | NIO | AIO
原文: http://my.oschina.net/bluesky0leon/blog/132361
BIO | NIO | AIO,本身的描述都是在Java語言的基礎(chǔ)上的。而描述IO,我們需要從兩個層面:
簡稱
-
BIO,同步阻塞式IO
-
NIO,同步非阻塞IO
-
AIO,異步非阻塞IO
BIO,同步阻塞式IO
NIO,同步非阻塞IO
AIO,異步非阻塞IO
從編程語言層面
IO的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO、異步非阻塞的AIO
?
在JDK1.4出來之前,我們建立網(wǎng)絡(luò)連接的時候采用BIO模式,需要先在服務(wù)端啟動一個ServerSocket,然后在客戶端啟動Socket來對服務(wù)端進行通信,默認(rèn)情況下服務(wù)端需要對每個請求建立一堆線程等待請求,而客戶端發(fā)送請求后,先咨詢服務(wù)端是否有線程相應(yīng),如果沒有則會一直等待或者遭到拒絕請求,如果有的話,客戶端會線程會等待請求結(jié)束后才繼續(xù)執(zhí)行。
BIO與NIO一個比較重要的不同,是我們使用BIO的時候往往會引入多線程,每個連接一個單獨的線程;而NIO則是使用單線程或者只使用少量的多線程,每個連接共用一個線程。
NIO的最重要的地方是當(dāng)一個連接創(chuàng)建后,不需要對應(yīng)一個線程,這個連接會被注冊到多路復(fù)用器上面,所以所有的連接只需要一個線程就可以搞定,當(dāng)這個線程中的多路復(fù)用器進行輪詢的時候,發(fā)現(xiàn)連接上有請求的話,才開啟一個線程進行處理,也就是一個請求一個線程模式。
在NIO的處理方式中,當(dāng)一個請求來的話,開啟線程進行處理,可能會等待后端應(yīng)用的資源(JDBC連接等),其實這個線程就被阻塞了,當(dāng)并發(fā)上來的話,還是會有BIO一樣的問題。
?
HTTP/1.1出現(xiàn)后,有了Http長連接,這樣除了超時和指明特定關(guān)閉的http header外,這個鏈接是一直打開的狀態(tài)的,這樣在NIO處理中可以進一步的進化,在后端資源中可以實現(xiàn)資源池或者隊列,當(dāng)請求來的話,開啟的線程把請求和請求數(shù)據(jù)傳送給后端資源池或者隊列里面就返回,并且在全局的地方保持住這個現(xiàn)場(哪個連接的哪個請求等),這樣前面的線程還是可以去接受其他的請求,而后端的應(yīng)用的處理只需要執(zhí)行隊列里面的就可以了,這樣請求處理和后端應(yīng)用是異步的.當(dāng)后端處理完,到全局地方得到現(xiàn)場,產(chǎn)生響應(yīng),這個就實現(xiàn)了異步處理。
- BIO是一個連接一個線程。
- NIO是一個請求一個線程。
- AIO是一個有效請求一個線程。
BIO
在JDK1.4之前,用Java編寫網(wǎng)絡(luò)請求,都是建立一個ServerSocket,然后,客戶端建立Socket時就會詢問是否有線程可以處理,如果沒有,要么等待,要么被拒絕。即:一個連接,要求Server對應(yīng)一個處理線程。
NIO
在JDK1.4及以后版本中提供了一套API來專門操作非阻塞I/O,我們可以在java.nio包及其子包中找到相關(guān)的類和接口。由于這套API是JDK新提供的I/O API,因此,也叫New I/O,這就是包名nio的由來。這套API由三個主要的部分組成:緩沖區(qū)(Buffers)、通道(Channels)和非阻塞I/O的核心類組成。在理解NIO的時候,需要區(qū)分,說的是New I/O還是非阻塞IO,New I/O是Java的包,NIO是非阻塞IO概念。這里講的是后面一種。
NIO本身是基于事件驅(qū)動思想來完成的,其主要想解決的是BIO的大并發(fā)問題: 在使用同步I/O的網(wǎng)絡(luò)應(yīng)用中,如果要同時處理多個客戶端請求,或是在客戶端要同時和多個服務(wù)器進行通訊,就必須使用多線程來處理。也就是說,將每一個客戶端請求分配給一個線程來單獨處理。這樣做雖然可以達到我們的要求,但同時又會帶來另外一個問題。由于每創(chuàng)建一個線程,就要為這個線程分配一定的內(nèi)存空間(也叫工作存儲器),而且操作系統(tǒng)本身也對線程的總數(shù)有一定的限制。如果客戶端的請求過多,服務(wù)端程序可能會因為不堪重負(fù)而拒絕客戶端的請求,甚至服務(wù)器可能會因此而癱瘓。
NIO基于Reactor,當(dāng)socket有流可讀或可寫入socket時,操作系統(tǒng)會相應(yīng)的通知引用程序進行處理,應(yīng)用再將流讀取到緩沖區(qū)或?qū)懭氩僮飨到y(tǒng)。
也就是說,這個時候,已經(jīng)不是一個連接就要對應(yīng)一個處理線程了,而是有效的請求,對應(yīng)一個線程,當(dāng)連接沒有數(shù)據(jù)時,是沒有工作線程來處理的。
Java中的包:java.nio.*、java.nio.channel.*、java.nio.file.*
AIO
與NIO不同,當(dāng)進行讀寫操作時,只須直接調(diào)用API的read或write方法即可。這兩種方法均為異步的,對于讀操作而言,當(dāng)有流可讀取時,操作系統(tǒng)會將可讀的流傳入read方法的緩沖區(qū),并通知應(yīng)用程序;對于寫操作而言,當(dāng)操作系統(tǒng)將write方法傳遞的流寫入完畢時,操作系統(tǒng)主動通知應(yīng)用程序。
即可以理解為,read/write方法都是異步的,完成后會主動調(diào)用回調(diào)函數(shù)。
在JDK1.7中,這部分內(nèi)容被稱作NIO.2,主要在java.nio.channels包下增加了下面四個異步通道:
- AsynchronousSocketChannel
- AsynchronousServerSocketChannel
- AsynchronousFileChannel
- AsynchronousDatagramChannel
其中的read/write方法,會返回一個帶回調(diào)函數(shù)的對象,當(dāng)執(zhí)行完讀取/寫入操作后,直接調(diào)用回調(diào)函數(shù)。
實現(xiàn)原理
說道實現(xiàn)原理,還要從操作系統(tǒng)的IO模型上了解
按照《Unix網(wǎng)絡(luò)編程》的劃分,IO模型可以分為:阻塞IO、非阻塞IO、IO復(fù)用、信號驅(qū)動IO和異步IO,按照POSIX標(biāo)準(zhǔn)來劃分只分為兩類:同步IO和異步IO。如何區(qū)分呢?首先一個IO操作其實分成了兩個步驟:發(fā)起IO請求和實際的IO操作,同步IO和異步IO的區(qū)別就在于第二個步驟是否阻塞,如果實際的IO讀寫阻塞請求進程,那么就是同步IO,因此阻塞IO、非阻塞IO、IO復(fù)用、信號驅(qū)動IO都是同步IO,如果不阻塞,而是操作系統(tǒng)幫你做完IO操作再將結(jié)果返回給你,那么就是異步IO。阻塞IO和非阻塞IO的區(qū)別在于第一步,發(fā)起IO請求是否會被阻塞,如果阻塞直到完成那么就是傳統(tǒng)的阻塞IO,如果不阻塞,那么就是非阻塞IO。
收到操作系統(tǒng)的IO模型,又不得不提select/poll/epoll/iocp,關(guān)于這四個的理解,不多做解釋,自己還沒理解到位。
可以理解的說明是:在Linux 2.6以后,java NIO的實現(xiàn),是通過epoll來實現(xiàn)的,這點可以通過jdk的源代碼發(fā)現(xiàn)。而AIO,在windows上是通過IOCP實現(xiàn)的,在linux上還是通過epoll來實現(xiàn)的。
這里強調(diào)一點:AIO,這是I/O處理模式,而epoll等都是實現(xiàn)AIO的一種編程模型;換句話說,AIO是一種接口標(biāo)準(zhǔn),各家操作系統(tǒng)可以實現(xiàn)也可以不實現(xiàn)。在不同操作系統(tǒng)上在高并發(fā)情況下最好都采用操作系統(tǒng)推薦的方式。Linux上還沒有真正實現(xiàn)網(wǎng)絡(luò)方式的AIO。
底層基礎(chǔ)
說到底層,要說Linux系統(tǒng)編程,這里自己也不熟悉,有待后來人補充了。
只籠統(tǒng)的說一個:AIO實現(xiàn)
在windows上,AIO的實現(xiàn)是通過IOCP來完成的,看JDK的源代碼,可以發(fā)現(xiàn)
WindowsAsynchronousSocketChannelImpl
看實現(xiàn)接口:
implements Iocp.OverlappedChannel
再看實現(xiàn)方法:里面的read0/write0方法是native方法,調(diào)用的jvm底層實現(xiàn),虛擬機技術(shù)不熟悉,不獻丑了。
在linux上,AIO的實現(xiàn)是通過epoll來完成的,看JDK源碼,可以發(fā)現(xiàn),實現(xiàn)源碼是:
UnixAsynchronousSocketChannelImpl
看實現(xiàn)接口:
implements Port.PollableChannel
這是與windows最大的區(qū)別,poll的實現(xiàn),在linux2.6后,默認(rèn)使用epoll。
這樣就可以理解了。
總結(jié)
以上是生活随笔為你收集整理的Java的IO:BIO | NIO | AIO的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快手服务通知怎么设置
- 下一篇: Java AIO初探(异步网络IO)