做餅的網(wǎng)站/人民網(wǎng)疫情最新消息
一、什么是 I/O?
?I/O 描述了計(jì)算機(jī)系統(tǒng)與外部設(shè)備(磁盤(pán))之間通信的過(guò)程。
為了保證操作系統(tǒng)的穩(wěn)定性和安全性,一個(gè)進(jìn)程的地址空間劃分為?用戶(hù)空間(User space)?和?內(nèi)核空間(Kernel space )?。用戶(hù)進(jìn)程(應(yīng)用程序)想要執(zhí)行 IO 操作的話(huà),必須通過(guò)?系統(tǒng)調(diào)用?來(lái)間接訪(fǎng)問(wèn)內(nèi)核空間。
當(dāng)應(yīng)用程序發(fā)起 I/O 調(diào)用后,會(huì)經(jīng)歷兩個(gè)步驟:
- 內(nèi)核等待 I/O 設(shè)備準(zhǔn)備好數(shù)據(jù)——階段①
- 內(nèi)核將數(shù)據(jù)從內(nèi)核空間拷貝到用戶(hù)空間——階段②
二、同步與異步、阻塞與非阻塞
阻塞與非阻塞是針對(duì) 線(xiàn)程 來(lái)說(shuō)的;同步與異步是針對(duì) 整個(gè)I/O操作 來(lái)說(shuō)的
同步阻塞IO(BIO):應(yīng)用程序發(fā)起請(qǐng)求后,需等待階段①和②都完成,在整個(gè)IO期間不能做別的,必須等待整個(gè)IO操作完成才可進(jìn)行下個(gè)任務(wù)。
同步非阻塞IO:應(yīng)用程序發(fā)起請(qǐng)求后,在階段①不斷輪詢(xún)內(nèi)核數(shù)據(jù)是否準(zhǔn)備好,在階段②需阻塞等待,在整個(gè)IO期間不能做別的,必須等待整個(gè)IO操作完成才可進(jìn)行下個(gè)任務(wù)。
異步阻塞IO:應(yīng)用程序發(fā)起請(qǐng)求后,立刻返回,在等待階段①和②期間也不做別的(因?yàn)樽枞麙炱甬?dāng)前線(xiàn)程),就等著內(nèi)核通知。內(nèi)核完成①和②之后,發(fā)起回調(diào),應(yīng)用程序可以直接處理數(shù)據(jù)。
異步非阻塞IO(AIO):應(yīng)用程序發(fā)起請(qǐng)求后,立刻返回,在等待階段①和②期間做別的事。內(nèi)核完成①和②之后,發(fā)起回調(diào),應(yīng)用程序可以直接處理數(shù)據(jù)。
簡(jiǎn)述JAVA同步、異步、阻塞和非阻塞之間的區(qū)別_java_腳本之家 (jb51.net)
什么是阻塞和非阻塞?什么是同步和異步?什么是BIO、NIO、AIO? - 沙灘de流沙 - 博客園 (cnblogs.com)
三、?I/O 多路復(fù)用模型?(NIO)
同步非阻塞IO的應(yīng)用程序不斷進(jìn)行 I/O 系統(tǒng)調(diào)用輪詢(xún)數(shù)據(jù)是否已經(jīng)準(zhǔn)備好的過(guò)程是十分消耗 CPU 資源的。
IO 多路復(fù)用模型,通過(guò)減少無(wú)效的系統(tǒng)調(diào)用,減少了對(duì) CPU 資源的消耗。
IO 多路復(fù)用模型中,線(xiàn)程首先發(fā)起 select 調(diào)用(階段①),詢(xún)問(wèn)內(nèi)核數(shù)據(jù)是否準(zhǔn)備就緒(一個(gè)線(xiàn)程管理多個(gè)客戶(hù)端連接,也就是在這期間詢(xún)問(wèn)每個(gè)連接),等內(nèi)核把數(shù)據(jù)準(zhǔn)備好了(某一個(gè)連接),用戶(hù)線(xiàn)程再發(fā)起 read 調(diào)用(階段②)。read 調(diào)用的過(guò)程(數(shù)據(jù)從內(nèi)核空間->用戶(hù)空間)還是阻塞的。
四、深入理解
同步與異步(是誰(shuí)通知消息)
同步:?發(fā)起一個(gè)調(diào)用后,被調(diào)用者未處理完請(qǐng)求之前,調(diào)用不返回。需要反復(fù)詢(xún)問(wèn)數(shù)據(jù)是否就緒。
異步:?發(fā)起一個(gè)調(diào)用后,立刻得到被調(diào)用者的回應(yīng)表示已接收到請(qǐng)求,但是被調(diào)用者并沒(méi)有返回結(jié)果,此時(shí)我們可以處理其他的請(qǐng)求,被調(diào)用者通常依靠事件,回調(diào)等機(jī)制來(lái)通知調(diào)用者其返回結(jié)果。
同步和異步的區(qū)別最大在于——異步是被調(diào)用者來(lái)通知調(diào)用者處理結(jié)果。同步需要調(diào)用者自己反復(fù)詢(xún)問(wèn)處理結(jié)果。
阻塞和非阻塞(線(xiàn)程等待消息通知時(shí)的可不可以做別的事)
阻塞:?發(fā)起一個(gè)請(qǐng)求,調(diào)用者一直等待請(qǐng)求結(jié)果返回,無(wú)法從事其他任務(wù),只有當(dāng)條件就緒才能繼續(xù)。
非阻塞:?發(fā)起一個(gè)請(qǐng)求,調(diào)用者不用一直等著結(jié)果返回,可以先去干其他事情。
同步非阻塞 和 異步非阻塞 區(qū)別
- 同步阻塞 是自己發(fā)起之后,就一直等待處理結(jié)果。
- 同步非阻塞 是自己等待消息通知,需自己反復(fù)詢(xún)問(wèn)處理結(jié)果。
- 異步非阻塞 是別人通過(guò)事件或回調(diào)機(jī)制來(lái)通知自己,自己不需要詢(xún)問(wèn)。
BIO(Socket 網(wǎng)絡(luò)編程)
- 同步阻塞I/O
- 一請(qǐng)求一線(xiàn)程
- 面向流(Stream)。數(shù)據(jù)直接讀寫(xiě)到 Stream 對(duì)象中。
- 不適合高并發(fā)場(chǎng)景
在 Java 虛擬機(jī)中,線(xiàn)程是寶貴的資源,線(xiàn)程的創(chuàng)建和銷(xiāo)毀成本很高,除此之外,線(xiàn)程的切換成本也是很高的。
痛點(diǎn)
- 處理多個(gè)客戶(hù)端請(qǐng)求,就必須使用多線(xiàn)程。如果這個(gè)連接不做任何事情的話(huà)就會(huì)造成不必要的線(xiàn)程開(kāi)銷(xiāo)。線(xiàn)程開(kāi)銷(xiāo)大。線(xiàn)程之間的切換也會(huì)浪費(fèi)資源開(kāi)銷(xiāo)。
- 如果并發(fā)訪(fǎng)問(wèn)量增加會(huì)導(dǎo)致線(xiàn)程數(shù)急劇膨脹可能會(huì)導(dǎo)致線(xiàn)程堆棧溢出、創(chuàng)建新線(xiàn)程失敗等問(wèn)題,最終導(dǎo)致進(jìn)程宕機(jī)或者僵死,不能對(duì)外提供服務(wù)。
優(yōu)化(偽異步IO)
- 可以通過(guò)?線(xiàn)程池機(jī)制?改善。當(dāng)有新的客戶(hù)端接入時(shí),將客戶(hù)端的 Socket 封裝成一個(gè)Task(該任務(wù)實(shí)現(xiàn)java.lang.Runnable接口)投遞到后端的線(xiàn)程池中進(jìn)行處理,JDK 的線(xiàn)程池維護(hù)一個(gè)消息隊(duì)列和 N 個(gè)活躍線(xiàn)程,對(duì)消息隊(duì)列中的任務(wù)進(jìn)行處理。由于線(xiàn)程池可以設(shè)置消息隊(duì)列的大小和最大線(xiàn)程數(shù),因此,它的資源占用是可控的,無(wú)論多少個(gè)客戶(hù)端并發(fā)訪(fǎng)問(wèn),都不會(huì)導(dǎo)致資源的耗盡和宕機(jī)。
- 避免了為每個(gè)請(qǐng)求都創(chuàng)建一個(gè)獨(dú)立線(xiàn)程造成的線(xiàn)程資源耗盡問(wèn)題。不過(guò)因?yàn)樗牡讓尤稳皇峭阶枞腂IO模型,因此無(wú)法從根本上解決問(wèn)題。
NIO(SocketChannel 網(wǎng)絡(luò)編程)
- I/O 多路復(fù)用模型
- 多通道一線(xiàn)程
- 面向緩沖區(qū)。所有數(shù)據(jù)讀寫(xiě)到緩沖區(qū)。
- 三大核心組件:
- Buffer(緩沖區(qū))。在NIO厙中,所有數(shù)據(jù)都是用緩沖區(qū)處理的。在讀取數(shù)據(jù)時(shí),它是直接讀到緩沖區(qū)中的; 在寫(xiě)入數(shù)據(jù)時(shí),寫(xiě)入到緩沖區(qū)中。任何時(shí)候訪(fǎng)問(wèn)NIO中的數(shù)據(jù),都是通過(guò)緩沖區(qū)進(jìn)行操作。
- Channel(通道)。NIO 通過(guò)Channel(通道) 進(jìn)行讀寫(xiě)。通道是雙向的,可讀也可寫(xiě),而流的讀寫(xiě)是單向的。無(wú)論讀寫(xiě),通道只能和Buffer交互。因?yàn)?Buffer,通道可以異步地讀寫(xiě)。
- Selector(選擇器)。NIO的選擇器用于使用單個(gè)線(xiàn)程管理多個(gè)通道。只有在通道里真正有讀寫(xiě)事件發(fā)生時(shí)(事件驅(qū)動(dòng)),才會(huì)交給線(xiàn)程讀寫(xiě)(Selector會(huì)一直詢(xún)問(wèn)每個(gè)通道有沒(méi)有讀寫(xiě)事件,這個(gè)過(guò)程是同步的),因此,它只需要較少的線(xiàn)程來(lái)處理這些通道。不必為每一個(gè)連接都創(chuàng)建一個(gè)線(xiàn)程,也不必去維護(hù)多個(gè)線(xiàn)程。避免了多個(gè)線(xiàn)程之間的上下文切換,導(dǎo)致資源的浪費(fèi)。(只有網(wǎng)絡(luò)IO才會(huì)使用選擇器,文件IO是不需要使用的)
- 適合高并發(fā)場(chǎng)景
痛點(diǎn)
- NIO的類(lèi)庫(kù)和API繁雜,學(xué)習(xí)成本高。
- 需要熟悉Java多線(xiàn)程編程。這是因?yàn)镹IO編程涉及到Reactor模式,你必須對(duì)多線(xiàn)程和網(wǎng)絡(luò)編程非常熟悉,才能寫(xiě)出高質(zhì)量的NIO程序。
-
JDK 的 NIO 底層由 epoll 實(shí)現(xiàn),該實(shí)現(xiàn)飽受詬病的空輪詢(xún) bug 會(huì)導(dǎo)致 cpu 飆升 100%
當(dāng)我們調(diào)用socket.read()、socket.write()這類(lèi)阻塞函數(shù)的時(shí)候,這類(lèi)函數(shù)不能立即返回,也無(wú)法中斷,需要等待socket可讀或者可寫(xiě),才會(huì)返回,因此一個(gè)線(xiàn)程只能處理一個(gè)請(qǐng)求。在這等待的過(guò)程中,cpu并不干活,(即阻塞住了),那么cpu的資源就沒(méi)有很好地利用起來(lái)。因此對(duì)于這種情況,我們使用多線(xiàn)程來(lái)提高cpu資源的利用率:在等待的這段時(shí)間,就可以切換到別的線(xiàn)程去處理事件,直到socket可讀或可寫(xiě)了,通過(guò)中斷信號(hào)通知cpu,再切換回來(lái)繼續(xù)處理數(shù)據(jù)。例如線(xiàn)程A正在等待socket可讀,而線(xiàn)程B已經(jīng)就緒了,那么就可以先切換到線(xiàn)程B去處理。雖然上下文切換也會(huì)花一些時(shí)間,但是遠(yuǎn)比阻塞在線(xiàn)程A這里空等要好。當(dāng)然計(jì)算機(jī)內(nèi)部實(shí)際的情況比這復(fù)雜得多。
而NIO的讀寫(xiě)函數(shù)可以立刻返回,這就給了我們不開(kāi)線(xiàn)程利用CPU的最好機(jī)會(huì):如果一個(gè)連接不能讀寫(xiě)(socket.read()返回0或者socket.write()返回0),我們可以把這件事記下來(lái)。因此只需要一個(gè)Selector不斷地輪詢(xún)這些事件,一旦有就緒的時(shí)間,處理即可。不需要多線(xiàn)程。
AIO
- 異步非阻塞I/O
- 異步 IO 是基于事件和回調(diào)機(jī)制實(shí)現(xiàn)的,也就是應(yīng)用操作之后會(huì)直接返回(發(fā)起請(qǐng)求后,線(xiàn)程直接返回干別的事,這個(gè)過(guò)程是異步的,只要等待操作完成后,通知該線(xiàn)程),不會(huì)堵塞在那里,當(dāng)后臺(tái)處理完成,操作系統(tǒng)會(huì)通知相應(yīng)的線(xiàn)程進(jìn)行后續(xù)的操作。
痛點(diǎn)
- 復(fù)雜性:雖然AIO的編程模型相對(duì)簡(jiǎn)單,但是由于其非阻塞的特性,編程復(fù)雜性可能會(huì)增加。例如,需要處理操作完成的通知,以及可能的并發(fā)問(wèn)題。
- 資源消耗:AIO可能會(huì)消耗更多的系統(tǒng)資源。因?yàn)槊總€(gè)操作都需要?jiǎng)?chuàng)建一個(gè)回調(diào)函數(shù),如果并發(fā)連接數(shù)非常大,可能會(huì)消耗大量的系統(tǒng)資源。
- 可移植性:AIO在某些平臺(tái)上可能不可用或者性能不佳。因此,如果需要跨平臺(tái)的可移植性,可能需要考慮使用其他I/O模型。
BIO適合連接數(shù)目較少且固定的架構(gòu)。
NIO適合連接數(shù)目多,但是并發(fā)讀寫(xiě)操作相對(duì)較少的場(chǎng)景。
AIO則適合連接數(shù)目多,且并發(fā)讀寫(xiě)操作也多的場(chǎng)景。
五、思考題
看看你現(xiàn)在能回答這些問(wèn)題了嗎?
- 簡(jiǎn)單說(shuō)下 BIO、NIO 和 AIO?具體使用、區(qū)別及原理?
- BIO,NIO,AIO的痛點(diǎn),怎么優(yōu)化?
- 為什么BIO比NIO性能差?簡(jiǎn)單講講區(qū)別?
- 假設(shè)有100個(gè)連接,采用NIO的方式要服務(wù)端要分配幾個(gè)線(xiàn)程,采用BIO的方式呢?
阿里畢玄-測(cè)試Java編程能力-我的回答(一)_java bio建立100個(gè)連接-CSDN博客
- 為啥要用異步IO不用多線(xiàn)程,不是一樣可以加速嗎?
- NIO的設(shè)計(jì)架構(gòu)?JDK中NIO有哪些重要組件?
- 同步、異步調(diào)用方式的具體實(shí)現(xiàn)