故事的背景:
有一個(gè)動物園寻拂,動物園里面有獅子析桥,老虎司草,猴子等動物,他們每天定期來吃不同的水果泡仗,而這些水果要由動物園管理員到貨場領(lǐng)取分配埋虹。
IO
我們知道,在使用IO的時(shí)候往往可以在服務(wù)器端使用多線程或者線程池來處理并發(fā)請求娩怎,這也叫偽異步搔课,那么故事開始了:
在貨場里有五個(gè)動物園管理員等待水果貨車的到來,他們一直在等待截亦,如果貨車不來他們也要死等爬泥,直到貨車的到來,這時(shí)候貨車來了崩瓤,每個(gè)管理員都領(lǐng)了一份水果去尋找動物袍啡,管理員A來到獅子旁邊,給獅子一份蘋果却桶,獅子開始吃境输,中途吃累了,休息了一會兒繼續(xù)吃颖系,這時(shí)候管理員A就一直等著獅子吃完嗅剖,再回去拿水果去另一個(gè)動物那里。五個(gè)管理員都是這樣做的嘁扼。
NIO
Java NIO是在jdk1.4開始使用的信粮,它既可以說成“新IO”,也可以說成非阻塞式I/O趁啸。下面是java NIO的工作原理:
由一個(gè)專門的線程來處理所有的IO事件强缘,并負(fù)責(zé)分發(fā)督惰。
事件驅(qū)動機(jī)制:事件到的時(shí)候觸發(fā),而不是同步的去監(jiān)視事件旅掂。
線程通訊:線程之間通過wait,notify等方式通訊姑丑。保證每次上下文切換都是有意義的。減少無謂的線程切換辞友。
那么故事開始了:動物園里栅哀,有一個(gè)管理員他先去了解動物們都喜歡吃什么樣的水果并做了一個(gè)登記,然后去貨場看一看貨車有沒有到來称龙,如果沒有來他就回去繼續(xù)干別的事留拾,就這樣每隔一段時(shí)間就去貨場看一下。這時(shí)候貨車來了鲫尊,他叫來另外五個(gè)管理員并告訴管理員哪些動物喜歡吃哪些水果痴柔,管理員們分類取不同的水果,然后各自去找動物們分發(fā)水果疫向,還是管理員A咳蔚,來到獅子旁邊把它喜歡吃的蘋果給它吃,獅子吃的比較慢搔驼,這時(shí)候管理員A說谈火,你先吃著,我給其他動物送水果舌涨,我一會兒再來取水果盤子糯耍,這時(shí)候管理員A回去取來香焦繼續(xù)猴子送去,在猴子吃香焦累了休息的時(shí)候囊嘉,管理員A來找獅子拿回水果盤子温技,再去找猴子拿回水果盤子。
不知道大家在看完這二個(gè)小故事之后有沒有理解NIO和IO扭粱,那么在下次分享的時(shí)候舵鳞,咱們將正式進(jìn)入NIO原理及源碼的分享,謝謝大家琢蛤。
概念
- 異步:某個(gè)事情需要10s完成蜓堕。而我只需要調(diào)用某個(gè)函數(shù)告訴xxx來幫我做(然后我再干其他的事情)
- 同步:某個(gè)事情需要10s完成,我需要一直等它完成(等10s)虐块,再能繼續(xù)后面的工作俩滥。
- 阻塞:做某件事情嘉蕾,直到完成贺奠,除非超時(shí)
- 非阻塞:嘗試做,如果不能做错忱,就不做(直接返回)儡率,如果能做挂据,就做。
前兩者和后兩者不容易區(qū)分儿普,不過前兩者更多的有涉及到多線程交互(消息)的場景崎逃。
舉個(gè)例子
小李喝了想喝水,于是去煮開水眉孩。
1个绍、小李把水壺放到爐子上,等待水燒開浪汪。(同步阻塞)
小李感覺這樣太費(fèi)時(shí)間巴柿。
2、小李把水壺放到爐子上死遭,去客廳看電視广恢,時(shí)不時(shí)去廚房看看水開沒有。(同步非阻塞)
小李還是覺得自己這樣太累呀潭,于是買了把會響笛的那種水壺钉迷。水開之后,能發(fā)出聲音钠署。
3糠聪、小李把響水壺放到爐子上,等待水壺發(fā)出聲音谐鼎。(異步阻塞)
覺得這樣傻等意義不大
5枷颊、小李把響水壺放到爐子上,去客廳看電視该面,水壺響之前不再去看它了夭苗,響了再去拿壺。(異步非阻塞)
這樣真好隔缀。
深入理解
阻塞就是 recv/read的時(shí)候 socket接收緩沖區(qū)要是有數(shù)據(jù)就讀题造, 沒數(shù)據(jù)我就一直睡覺賴著不走,直到有數(shù)據(jù)來了讀完我才走猾瘸。send/write的時(shí)候界赔,要是發(fā)送緩沖區(qū)滿了,沒有空間繼續(xù)發(fā)送了我也一直睡覺賴著不走牵触,直到發(fā)送緩沖區(qū)騰出足夠的空間讓我把數(shù)據(jù)全部塞到發(fā)送緩沖區(qū)里我才走淮悼。(當(dāng)然如果你通過setsockopt設(shè)置了讀寫超時(shí),超時(shí)時(shí)間到了還是會返回-1和EAGAIN揽思,不再睡覺等待)
非阻塞就是recv/read的時(shí)候袜腥,要是接收緩沖區(qū)有數(shù)據(jù)我就讀完,沒有數(shù)據(jù)我直接帶著返回的-1和EGAIN走人钉汗,絕不睡覺等待耽誤時(shí)間羹令。write/send的時(shí)候鲤屡, 要是發(fā)送緩沖區(qū)有足夠的空間,就立刻把數(shù)據(jù)塞到發(fā)送緩沖區(qū)去福侈,然后走人酒来,如果發(fā)送緩存區(qū)滿了,空間不足肪凛,那直接帶著返回的-1和EAGAIN走人堰汉。
至于IO多路復(fù)用,首先要理解的是伟墙,操作系統(tǒng)為你提供了一個(gè)功能衡奥,當(dāng)你的某個(gè)socket接收緩存區(qū)有數(shù)據(jù)可讀,或者發(fā)送緩沖區(qū)有空間可寫的時(shí)候远荠,它可以給你一個(gè)通知矮固。這樣當(dāng)配合非阻塞的socket使用時(shí),只有當(dāng)系統(tǒng)通知我哪個(gè)描述符可讀了譬淳,我才去執(zhí)行read操作档址,可以保證每次read都能讀到有效數(shù)據(jù)而不做純返回-1和EAGAIN的無用功。寫操作類似邻梆。操作系統(tǒng)的這個(gè)功能通過select/poll/epoll之類的系統(tǒng)調(diào)用函數(shù)來使用守伸,這些函數(shù)都可以同時(shí)監(jiān)視多個(gè)描述符的讀寫就緒狀況,這樣浦妄,多個(gè)描述符的I/O操作都能在一個(gè)線程內(nèi)完成尼摹,這就叫I/O多路復(fù)用,這里的“復(fù)用”指的是復(fù)用同一個(gè)線程剂娄。
至于事件驅(qū)動蠢涝,其實(shí)是I/O多路復(fù)用的一個(gè)另外的稱呼。