
引出下面一段故事......
kenny:io包現在已經這麼優雅完美了,别改壞了!
kyle:不不不,我們肯定遵循開閉原則(對修改關閉,對擴充開放),是以不會随意修改io的,那io接口有什麼問題沒有?
kenny:除了有人說它過度設計之外,好像沒什麼呀,當你了解裝飾模式後你會覺得它還是很好了解的啊。
kyle:你不認為網絡原因或者其他原因碰到阻塞的io會使程式可用性大大降低嗎?
kenny:這個我知道,比如我去讀取檔案中的一行資料:
當一個線程在執行這段代碼, 遇到readLine()方法的時候, 需要等待資料從硬碟進入記憶體, 這個線程就會被阻塞, 當然我覺得這也沒啥, 很正常啊, 大家不都是這麼程式設計的嗎?
kyle:不, 有很多人反映了這個問題, 大家想把它改成非阻塞的, 也就是說調用了readLine()方法以後,立刻就傳回, 線程就可以幹别的事情去了。
kenny:為什麼要去幹别的事情? 這個線程不就是為了讀資料嗎? 好,就如你所說, 現在變成了非阻塞的, 調用了readLine()以後, 線程可以執行後面的代碼了, 那這個線程豈不還得做個輪詢操作,看看資料是不是已經讀好了?
這樣一來,代碼多醜陋啊, 不符合我一貫的美學原則, 還不如我原來的阻塞方式呢,我覺得你的要求不可思議。
kyle:要是你的線程打開了成百上千個檔案呢? 你這種阻塞的方式,隻能按照檔案1, 檔案2, 檔案3...... 順序的讀取這麼多檔案,太慢了. 如果是非阻塞方式, 你可以同時發起成百上千個讀操作(讀操作是由DMA(Direct Memory Access,直接記憶體讀取),不消耗cpu,非使用者态操作。關于使用者态和核心态下一篇會講到), 然後在那個循環中檢查, 看看誰的資料準備好了,就讀取誰的, 效率多高啊。
kenny:誰會那麼傻, 用一個線程打開這麼多檔案?肯定開多個線程呀。
kyle:那~你聽說過伺服器端的Socket程式設計嗎?
kenny:我當然知道, 我還知道這個情況: 一個socket連接配接來了, 就建立一個新的線程或者從線程池配置設定一個線程去處理這個連接配接。
kyle:那要是并發連接配接數太多了怎麼辦?
kenny:那就多建立線程呗。
kyle:每個線程都會占用記憶體空間, 數量多了系統受不了,線程之間的切換也是個要命的開銷啊(線程切換需要cpu在使用者态和核心态之間切換,cpu對于各線程變量或資料的存儲讀取需要消耗大量時間)。
kyle看着kenny沉默不語若有所思的樣子,趕緊稱熱打鐵補了一句:
是以大家呼喚非阻塞的方式啊, 讓一個線程管理成百上千個sockcet連接配接,就像管理多個檔案一樣,這樣就不用做線程切換了。
kenny:我似乎有點明白了, 正常情況下, 在某一個時刻, 不是每個socket 都有資料讀寫, 很多時候都是空閑的,是以完全可以用輪詢的方式來檢視那些socket可以讀寫, 進行操作就可以了。
kyle:好吧, 你想怎麼改?
總結:
網上關于nio和io的比較都是在比速度,小碼農翻牆查了下外網,發現老外關注的nio并不是速度問題,關于讀取速度面向緩存或者是bio的位元組讀取還是nio的塊級讀取,這些都是附加功能,并不是核心,核心是為了高可用,即可擴充。
選用宗量還應考慮很多東西:
- 客戶的生命時間?短期還是長期?
- 每個客戶預計的資料量?很多小塊或幾塊巨大的塊?
- 預計會同時連接配接多少用戶端?
- 您使用的是什麼作業系統以及您使用的JVM是什麼?這兩個因素分為線程和輪詢成本。
簡單來說,(長連接配接)高并發,低帶寬,高擴充,考慮nio;(短連接配接)低并發,高帶寬,考慮bio即普通io;
加我微信進技術交流群,各大教學資源免費領取,大廠内推!關注訂閱号:一隻可愛的小碼農,不定時推送高品質學習文章。