為什麼nio效率會比bio高?

目前就知道nio一個線程可以對應多個連接,bio一個線程對應一個連接,但是感覺bio除了多了一些線程,並不比nio多佔用多少資源呀,bio效率低是因為線程切換導致的么?


簡單的說,大量線程帶來的開銷包括:

1. 線程的內存開銷

2. 線程上下文切換的開銷,包括保存和載入上下文,以及由於上下文切換導致的緩存不命中。

參見:

I/O多路復用技術(multiplexing)是什麼? - 知乎epoll到底是如何做到少量線程同時接收多個請求事件處理部分到底放在哪裡? - 知乎


就是多的那個線程導致的。線程切換的開銷比用戶程序自己維護 app-specific 的狀態要大得多。


首先明確一點,技術沒有優劣,只有適不適合,能解決當前問題的技術就是好技術。BIO不一定比NIO效率差,就和單線程不一定比多線程效率低一樣。

舉個通俗例子吧,假如有一個飯店,只有一個廚師,如果每天只來一個客人,廚師一個人接待、做飯、上菜和結賬,效率很好(沒有中間人,直接接受顧客消息)。如果有十個同樣的廚師和顧客,效率也是很高。如果顧客再多,廚師無法增加,這個時候一個廚師得服務多個顧客,顧客只能排隊,而顧客也不知道啥時候能排到自己,只能死等,不時去問廚師輪到自己沒有,這就是NIO。假如你去的是肯德基,流水很快,你等等也無妨,反而你付款後到外面做其他事情會影響效率,NIO這時候依然很快。NIO可以總結為「我要讀」,這裡是「我要點菜」。如果在一個海鮮酒樓,那就很坑了,你肯定不樂意一直等,這時候怎麼提高效率呢?加一個服務員呀,如果沒空位,你先把聯繫方式給服務員,自己就可以去干其他事情了,服務員發現有空餘廚師的時候,她會通知你,告訴你可以準備點餐了。這就是NIO,總結一下就是「可以讀」了,這裡是「我可以點餐了」。如果是非同步NIO就更好了,成了「您要的菜已經好了」。

本質上,NIO是邏輯分層、職責單一、空間換時間的體現。如上例子就是,除了廚師,加入服務員、傳菜員,收銀員等角色;每個角色熟練本質工作,不用頻繁切換角色造成浪費(如換廚師服);角色增加,人員增加,空間消耗必然增大。


假如有10000個連接,4核CPU ,那麼bio 就需要一萬個線程,而nio大概就需要5個線程(一個接收請求,四個處理請求)。如果這10000個連接同時請求,那麼bio就有10000個線程搶四個CPU ,幾乎每個CPU 平均執行2500次上下文切換,而nio 四個處理線程,幾乎每個線程都對應一個CPU ,也就是幾乎沒有上下文切換。效率就體現出來了。


節省了大量的調度開銷


主要的區別在於,使用BIO的模型,在處理少量的鏈接的時候是完全可以的,但是由於BIO的特性,所以必須要一個連接一個線程,所以在處理大量連接的時候,這種方式就不行了。

而對於NIO的這一套模型,因為只有少量的線程在等待已經準備好了需要處理的連接,所以大量的線程都其實很輕鬆的,也不存在大量的線程切換。使得可以使用少量的線程可以處理大量的連接。

對應的內容在《Netty實戰》這本書的線程模型這一章有很細緻的討論。

您可以購買我翻譯的《Netty實戰》這本書。

如何評價《Netty實戰》這本書? - 知乎

京東預售鏈接(優先發貨):《Netty實戰》([美]諾曼·毛瑞爾(Norman Maurer),馬文·艾倫·沃爾夫泰爾(Marvin Allen Wolfthal))

java的nio和bio的異同? - 知乎


首先要明確一點:nio的適用於io高並發場景

線程開銷包括:內存開銷(創建一個線程需要為其分配和維護一個tcb線程式控制制塊保存上下文,一個線程私有的棧)、線程上下文切換的時間開銷(保存上下文,載入下一個線程的上下文),在高並發環境下多出來的這一點點開銷積累起來是非常可觀的。

若使用bio,高並發必然引起激烈contention,導致大量的上下文切換,若採用一個io一個線程模型,一個線程M級的空間消耗,內存消耗不起。而netty採用nio加selector加線程池基本上解決了上述問題:一是線程池限制了內存消耗,二是採用selector

使得只有處於活動狀態的io才會去佔用線程,不會使線程白白因為io而阻塞,提高了線程利用率。


淺談Java為什麼需要NIO


bio阻塞在讀寫時資源沒有準備好,只能切換,nio是多路復用,阻塞在select輪詢,真正讀寫的工作線程是不阻塞的


Nio的主要優勢是在於一個線程處理多個連接,防止給每一個連接新開線程。所以在處理大量連接時一般推薦使用nio,對於單個連接而言,nio效率反而可能比bio會低


如果網路條件好到爆,換句話說io開銷小到可以忽略不計的話,bio的效率會比nio更高


就好比醫院的挂號窗口,如果nio的方式大家排隊就好了,開5個窗口能支持100個人來辦理業務,但如果是bio,就必須開100個窗口,否則其他人連排隊的機會都沒有,窗口就相當於線程數,如果開的過多,銀行就炸掉了,如果剛開始開5個,多餘5個就新開窗口,醫院的窗口就是建了拆拆了建,你說耗時不


非同步IO還有一個優勢容易將業務邏輯放在一個線程中處理,即避免了線程切換,同時也避免的數據加鎖。多線程編程是服務端編程最會掉坑的地方。當你的架構的邏輯部分是單線程模式,同時還是高效率的非同步IO,就偷著樂吧


推薦閱讀:

TAG:操作系統 | Java | CC | Netty |