天天看點

這是我見過最簡單了解NIO的文章了

來源:http://www.javaranger.com/archives/1767

假設某銀行隻有10個職員。該銀行的業務流程分為以下4個步驟:

1) 顧客填申請表(5分鐘);

2) 職員稽核(1分鐘);

3) 職員叫保安去金庫取錢(3分鐘);

4) 職員列印票據,并将錢和票據傳回給顧客(1分鐘)。

我們看看銀行不同的工作方式對其工作效率到底有何影響。

1 BIO方式

每來一個顧客,馬上由一位職員來接待處理,并且這個職員需要負責以上4個完整流程。當超過10個顧客時,剩餘的顧客需要排隊等候。

我們算算這個銀行一個小時到底能處理多少顧客?一個職員處理一個顧客需要10分鐘(5+1+3+1)時間,一個小時(60分鐘)能處理6個顧客,一共10個職員,那就是隻能處理60個顧客。

可以看到銀行職員的工作狀态并不飽和,比如在第1步,其實是處于等待中。

這種工作其實就是BIO,每次來一個請求(顧客),就配置設定到線程池中由一個線程(職員)處理,如果超出了線程池的最大上限(10個),就扔到隊列等待 。

2 NIO方式

如何提高銀行的吞吐量呢?

思路:分而治之,将任務拆分開來,由專門的人負責專門的任務。

具體來講,銀行專門指派一名職員A,A的工作就是每當有顧客到銀行,他就遞上表格讓顧客填寫,每當有顧客填好表後,A就将其随機指派給剩餘的9名職員完成後續步驟。

我們計算下這種工作方式下銀行一個小時到底能處理多少顧客?

假設顧客非常多,職員A的工作處于飽和中,他不斷的将填好表的顧客帶到櫃台處理,櫃台一個職員5分鐘能處理完一個顧客,一個小時9名職員能處理:9*(60/5)=108。

可見工作方式的轉變能帶來效率的極大提升。

這種工作方式其實就NIO的思路。下圖是非常經典的NIO說明圖,mainReactor線程負責監聽server socket,accept新連接配接,并将建立的socket分派給subReactor;subReactor可以是一個線程,也可以是線程池(一般可以設定為CPU核數),負責多路分離已連接配接的socket,讀寫網絡資料,這裡的讀寫網絡資料可類比顧客填表這一耗時動作,對具體的業務處理功能,其扔給worker線程池完成。

可以看到典型NIO有三類線程,分别是mainReactor線程、subReactor線程、work線程。不同的線程幹專業的事情,最終每個線程都沒空着,系統的吞吐量自然就上去了。

這是我見過最簡單了解NIO的文章了

3 異步方式

第二種工作方式有沒有什麼可以提高的地方呢?

仔細檢視可發現第3步驟這3分鐘櫃台職員是在等待中度過的,那怎麼能讓櫃台職員保持滿負荷呢?

還是分而治之的思路,指派1個職員B來專門負責第3步驟。每當櫃台員工完成第2步時,就通知職員B來負責與保安溝通取錢。這時候櫃台員工可以繼續處理下一個顧客。當職員B拿到錢之後,他會怎麼辦呢?他會通知顧客錢已經到櫃台了,讓顧客重新排隊處理,當櫃台職員再次服務該顧客時,發現該顧客前3步已經完成,直接執行第4步即可。

我們可以算算通過這種方法,銀行的吞吐量能提高到多少。

假設職員B的工作非常飽和,櫃台一個職員現在2分鐘能處理完一個顧客,一個小時8名職員能處理:8*(60/2)=240。

在當今web服務中,經常需要通過RPC或者Http等方式調用第三方服務,這裡對應的就是第3步,如果這步耗時較長,通過異步方式将能極大降低資源使用率。

jetty Continuations 就實作了上述異步方式,有興趣的同學可以去嘗試下(http://wiki.eclipse.org/Jetty/Feature/Continuations)。

NIO+異步的方式能讓少量的線程(資源)做大量的事情,這适用于很多應用場景,比如代理服務、api服務、長連接配接服務等等,這些應用如果用同步方式将耗費大量機器資源。盡管NIO+異步能提高系統吞吐量,但其并不能讓一個請求的等待時間下降,相反可能會增加等待時間。

4 小結

總結就一句:“分而治之,将任務拆分開來,由專門的人負責專門的任務”,這不僅在計算機領域生效,在整個社會領域都生效。

繼續閱讀