1.阻塞IO模型
最傳統(tǒng)的IO模型昨忆,就是在讀和寫(xiě)的過(guò)程中發(fā)生阻塞現(xiàn)象读规。
用戶(hù)線程發(fā)起IO請(qǐng)求之后抓督,內(nèi)核會(huì)去檢查數(shù)據(jù)是否已就緒。
如果未就緒束亏,內(nèi)核就會(huì)等待數(shù)據(jù)就緒铃在,用戶(hù)線程就會(huì)掛起,出讓CPU。
當(dāng)內(nèi)核數(shù)據(jù)就緒定铜,內(nèi)核就會(huì)將數(shù)據(jù)拷貝到用戶(hù)線程阳液,并喚醒用戶(hù)線程,解除阻塞狀態(tài)揣炕。
典型的阻塞 IO 模型的例子為:
data = socket.read();
如果數(shù)據(jù)沒(méi)有就緒帘皿,就會(huì)一直阻塞在 read 方法。
2.非阻塞 IO 模型
當(dāng)用戶(hù)線程發(fā)起一個(gè)read操作的時(shí)候畸陡,不會(huì)阻塞鹰溜,而是立馬得到一個(gè)結(jié)果。
如果結(jié)果是一個(gè)error丁恭,用戶(hù)線程就知道數(shù)據(jù)還沒(méi)準(zhǔn)備好曹动,又繼續(xù)請(qǐng)求read操作。
一旦數(shù)據(jù)就緒牲览,用戶(hù)線程又繼續(xù)發(fā)起read操作墓陈,內(nèi)核就會(huì)拷貝數(shù)據(jù)到用戶(hù)線程并返回。
也就數(shù)說(shuō)第献,非阻塞IO實(shí)際上是不斷的輪詢(xún)檢查數(shù)據(jù)是否準(zhǔn)備好贡必,而不會(huì)出讓CPU。
典型的非阻塞IO例子:
while(true){
data = socket.read();
if(data!= error){
// 處理數(shù)據(jù)
break;
}
}
這種方式痊硕,不斷的輪詢(xún)檢查赊级,cpu占用率就非常高。
3.多路復(fù)用 IO模型
多路復(fù)用IO模型是目前用的比較多的IO模型岔绸,比如Redis就是用該模型理逊,java中的NIO也是用該模型。
多路復(fù)用IO模型就是有一個(gè)專(zhuān)門(mén)的線程負(fù)責(zé)管理所有的socket狀態(tài)盒揉,這個(gè)線程去輪詢(xún)多個(gè)socket的請(qǐng)求晋被,只有socket有真正的讀或?qū)懻?qǐng)求的時(shí)候,才會(huì)發(fā)起IO操作刚盈。
在多路復(fù)用IO模型中羡洛,只用一個(gè)線程來(lái)專(zhuān)門(mén)處理客戶(hù)端連接,所以不必像前兩種一樣藕漱,一旦有客戶(hù)端連接就新建一個(gè)線程欲侮,而且只有真正的客戶(hù)端讀寫(xiě)請(qǐng)求才會(huì)發(fā)起IO操作,這樣就大大減少了資源的使用和占用肋联。
在java nio中是用 selector.select()去查詢(xún)有沒(méi)有可可發(fā)起IO操作的通道威蕉,沒(méi)有的就阻塞,一旦有一個(gè)用戶(hù)線程有讀寫(xiě)請(qǐng)求橄仍,就對(duì)該線程發(fā)起IO操作韧涨。
多路復(fù)用IO模型也是通過(guò)輪詢(xún)的方式來(lái)檢查數(shù)據(jù)是否準(zhǔn)備牍戚,不過(guò)他只有一個(gè)線程而且是在內(nèi)核中輪詢(xún)檢查的,非阻塞IO是每個(gè)用戶(hù)線程各自輪詢(xún)檢查虑粥,顯然資源占用率低如孝,效率也會(huì)比較高。
要注意的是娩贷,多路復(fù)用IO畢竟還是用輪詢(xún)的方式第晰,一旦處理時(shí)間很長(zhǎng)或者響應(yīng)體很大響應(yīng)時(shí)間很長(zhǎng),就會(huì)影響排隊(duì)中的線程的等待時(shí)間彬祖。
4.信號(hào)驅(qū)動(dòng)IO模型
當(dāng)用戶(hù)線程發(fā)起一個(gè)io請(qǐng)求的時(shí)候但荤,會(huì)在socekt上注冊(cè)一個(gè)信號(hào)函數(shù),用戶(hù)線程會(huì)繼續(xù)執(zhí)行它的流程涧至,當(dāng)數(shù)據(jù)準(zhǔn)備就緒,內(nèi)核會(huì)發(fā)送一個(gè)信號(hào)給用戶(hù)線程桑包,用戶(hù)線程就會(huì)執(zhí)行信號(hào)函數(shù)中的邏輯來(lái)發(fā)起IO操作南蓬。
5.異步IO模型
用戶(hù)線程發(fā)起read操作的時(shí)候,立即去執(zhí)行其他業(yè)務(wù)哑了。內(nèi)核收到一個(gè)異步讀的請(qǐng)求之后赘方,就開(kāi)始等待數(shù)據(jù)準(zhǔn)備完成,數(shù)據(jù)準(zhǔn)備完成之后弱左,內(nèi)核會(huì)將數(shù)據(jù)拷貝到用戶(hù)線程窄陡,拷貝完之后在通知用戶(hù)線程數(shù)據(jù)已經(jīng)讀完。跟信號(hào)驅(qū)動(dòng)不同的是拆火,信號(hào)驅(qū)動(dòng)只是收到一個(gè)信號(hào)跳夭,再去用信號(hào)函數(shù)讀。異步IO是內(nèi)核直接給你们镜,在跟你說(shuō)好了币叹。
小結(jié)
阻塞IO:用戶(hù)線程阻塞掛起
非阻塞IO:用戶(hù)線程不阻塞了但是多個(gè)用戶(hù)線程一直占用CPU
多路復(fù)用IO:只有一個(gè)后臺(tái)線程占用cpu但是處理時(shí)間久的話(huà),其他用戶(hù)線程等待時(shí)間較長(zhǎng)
信號(hào)驅(qū)動(dòng)IO:線程可以立即去處理其他業(yè)務(wù)模狭,但是還需要通過(guò)信號(hào)函數(shù)來(lái)發(fā)起IO操作
異步IO:不需要信號(hào)函數(shù)發(fā)起IO操作了颈抚,而是內(nèi)核直接拷貝到用戶(hù)線程并通知
6.java io包
7.java nio包
8.java nio主要內(nèi)容
傳統(tǒng)的io是基于字節(jié)流或字符流來(lái)操作的,而java nio是基于緩沖和通道來(lái)操作的嚼鹉。
java nio主要包括:channel(通道)贩汉、buffer(緩沖)、selector(選擇器)
也就是說(shuō)锚赤,數(shù)據(jù)都是從通道讀到緩存區(qū)匹舞,或者從緩存區(qū)寫(xiě)到通道,而選擇器監(jiān)聽(tīng)著多個(gè)通道的事件比如:鏈接打開(kāi)事件宴树,數(shù)據(jù)到達(dá)事件策菜,所以單個(gè)線程可以監(jiān)聽(tīng)多個(gè)通道。
另外,java io和java nio最大的區(qū)別就是又憨,io是面向流的翠霍,nio是面向緩沖的
Channel:channel和io中的stream差不多是一個(gè)級(jí)別的,只不過(guò)stream是單向的比如:InputStream(讀)蠢莺,OutputStream(寫(xiě))寒匙,而channel是雙向的既可以讀也可以寫(xiě)。
Buffer:buffer實(shí)際上就是一個(gè)容器躏将,底層是一個(gè)連續(xù)數(shù)組锄弱。channel從文件或網(wǎng)絡(luò)中讀到的數(shù)據(jù)必須先經(jīng)過(guò)buffer。
如上圖:客戶(hù)端發(fā)送請(qǐng)求先經(jīng)過(guò)buffer在統(tǒng)一到channel祸憋,服務(wù)端接受數(shù)據(jù)也是先經(jīng)過(guò)channel統(tǒng)一讀到buffer中在進(jìn)行處理会宪。
selector:selector是nio中核心的類(lèi),一個(gè)selector能夠注冊(cè)多個(gè)channel蚯窥,并且能夠檢測(cè)出多個(gè)channel的事件掸鹅,只有真正有讀寫(xiě)事件的時(shí)候才會(huì)發(fā)起io操作。這樣依賴(lài)只要用一個(gè)線程就能管理多個(gè)連接拦赠,避免線程的上下文切換巍沙,減少系統(tǒng)的開(kāi)銷(xiāo)。
9.nio中的緩存區(qū)
java io是面向字節(jié)或字符的荷鼠,意味著每次讀寫(xiě)都是一個(gè)字節(jié)或多個(gè)字節(jié)句携,頻繁的刷盤(pán),而且沒(méi)有緩存到一個(gè)地方也沒(méi)辦法前后移動(dòng)流的數(shù)據(jù)允乐。nio中的緩存區(qū)就是為了解決這些問(wèn)題矮嫉,每次讀寫(xiě)都將數(shù)據(jù)暫存到緩存區(qū)然后在批量刷盤(pán),而且還能夠自由的前后移動(dòng)讀取緩存中的數(shù)據(jù)牍疏,增加了數(shù)據(jù)處理的效率和靈活性敞临。