同步和異步區別:有無通知(是否輪詢)
堵塞和非堵塞區別:操作結果是否等待(是否馬上有返回值),只是設計方式的不同
1,同步和異步是針對應用程序和內核的交互而言的。
2,阻塞和非阻塞是針對于進程在訪問數據的時候,根據IO操作的就緒狀態來采取的不同方式,說白了是一種讀取或者寫入操作函數的實現方式,阻塞方式下讀取或者寫入函數將一直等待,而非阻塞方式下,讀取或者寫入函數會立即返回一個狀態值。
由上描述基本可以總結一句簡短的話,同步和異步是目的,阻塞和非阻塞是實現方式。
1:同步指的是用戶進程觸發IO操作并等待或者輪詢的去查看IO操作是否就緒. ? ??自己上街買衣服,自己親自干這件事,別的事干不了。
2:異步異步是指用戶進程觸發IO操作以后便開始做自己的事情,而當IO操作已經完成的時候會得到IO完成的通知(異步的特點就是通知) ? ?告訴朋友自己合適衣服的尺寸,大小,顏色,讓朋友委托去賣,然后自己可以去干別的事。(使用異步IO時,Java將IO讀寫委托給OS處理,需要將數據緩沖區地址和大小傳給OS)
3:阻塞所謂阻塞方式的意思是指, 當試圖對該文件描述符進行讀寫時, 如果當時沒有東西可讀,或者暫時不可寫, 程序就進入等待 狀態, 直到有東西可讀或者可寫為止. ? ? 去公交站充值,發現這個時候,充值員不在(可能上廁所去了),然后我們就在這里等待,一直等到充值員回來為止。(當然現實社會,可不是這樣,但是在計算機里確實如此。)
4非阻塞狀態下, 如果沒有東西可讀, 或者不可寫, 讀寫函數馬上返回, 而不會等待,銀行里取款辦業務時,領取一張小票,領取完后我們自己可以玩玩手機,或者與別人聊聊天,當輪我們時,銀行的喇叭會通知,這時候我們就可以去了。
同步阻塞IO(JAVA BIO):
同步并阻塞,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。
同步非阻塞IO(Java NIO) :
NIO 有一個主要的類Selector,這個類似一個觀察者,只要我們把需要探知的socketchannel告訴Selector,我們接著做別的事情,當有事件發生時,他會通知我們,傳回一組SelectionKey,我們讀取這些Key,就會獲得我們剛剛注冊過的socketchannel,然后,我們從這個Channel中讀取數據,接著我們可以處理這些數據。
?同步非阻塞,服務器實現模式為一個請求一個線程,即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有I/O請求時才啟動一個線程進行處理。用戶進程也需要時不時的詢問IO操作是否就緒,這就要求用戶進程不停的去詢問。
Java對BIO、NIO、AIO的支持:
Java BIO : 同步并阻塞,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。
Java NIO : 同步非阻塞,服務器實現模式為一個請求一個線程,即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有I/O請求時才啟動一個線程進行處理。
Java AIO(NIO.2) :在此種模式下,用戶進程只需要發起一個IO操作然后立即返回,等IO操作真正的完成以后,應用程序會得到IO操作完成的通知,此時用戶進程只需要對數據進行處理就好了,不需要進行實際的IO讀寫操作,因為真正的IO讀取或者寫入操作已經由內核完成了。
BIO、NIO、AIO適用場景分析:
BIO方式適用于連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,并發局限于應用中,JDK1.4以前的唯一選擇,但程序直觀簡單易理解。
NIO方式適用于連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,并發局限于應用中,編程比較復雜,JDK1.4開始支持。
AIO方式使用于連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與并發操作,編程比較復雜,JDK7開始支持。
Recotor
反應器(Reactor)模式是為了處理一個或多個客戶端同時提交的服務請求而設計的。事件驅動的應用程序可以使用反應器結構化模式,多路分解并分配從一個或多個客戶端發給應用程序的服務請求。該模式的別名有:分配器(Dispatcher),通知器(Notifier)
以下例子摘自:http://daimojingdeyu.iteye.com/blog/828696
先用比較直觀的方式來介紹一下這種方式的優點,通過和常用的多線程方式比較一下,可能更好理解。
以去飯店吃飯為例,每一伙人來就餐就是一個事件,吃飯的人會先看一下菜單,然后點菜。處理這些就餐事件的就需要我們的服務人員了。每個服務員相當于一個線程。
多線程處理的方式會是這樣的:
一個人來就餐,一個服務員去服務,然后客人會看菜單,點菜。 服務員將菜單給后廚。
二個人來就餐,二個服務員去服務……
五個人來就餐,五個服務員去服務……
這個就是多線程的處理方式,一個事件到來,就會有一個線程服務。很顯然這種方式在人少的情況下會有很好的用戶體驗,每個客人都感覺自己是VIP,專人服務的。如果飯店一直這樣同一時間最多來5個客人,這家飯店是可以很好的服務下去的。
來了一個好消息,因為這家店的服務好,吃飯的人多了起來。同一時間會來10個客人,老板很開心,但是只有5個服務員,這樣就不能一對一服務了,有些客人就要沒有人管了。老板就又請了5個服務員,現在好了,又能每個人都受VIP待遇了。
越來越多的人對這家飯店滿意,客源又多了,同時來吃飯的人到了20人,老板高興不起來了,再請服務員吧,占地方不說,還要開工錢,再請人就攢不到錢了。怎么辦呢?老板想了想,10個服務員對付20個客人也是能對付過來的,服務員勤快點就好了,伺候完一個客人馬上伺候另外一個,還是來得及的。綜合考慮了一下,老板決定就使用10個服務人員的線程池啦~~~
但是這樣有一個比較嚴重的缺點就是,如果正在接受服務員服務的客人點菜很慢,其他的客人可能就要等好長時間了。有些火爆脾氣的客人可能就等不了走人了。
Reactor就可以很好的解決這個問題。
因為點菜才通常是很耗時的,所以當有人來吃飯的時候,可以先把菜單交給點菜的人自己瀏覽,等點菜的人想好了要點的菜的時候再招呼服務員,等服務員過來了之后就可以為顧客點菜并發送到后廚了。這個在某種意義上說就是用單線程在做多線程的事情。
Reactor的事件驅動就體現在了只有當事件發生(客戶招呼服務員點菜)的時候,服務員(線程)才去處理。而客戶剛進入飯店的時候,是不需要去處理的。
從這個簡單的例子應該可以基本明白Reactor是干什么的了吧。(由事件觸發,并分發請求)
2.2Reactor模式的思想:分而治之+事件驅動
分而治之:
一個connection里發生的完整的網絡處理過程一般分為accept、read、decode、compute、encode、send這幾步。Reactor將每個步驟映射為一個task,服務端端的線程執行的最小邏輯單元不再是一次完整的網絡處理過程,而是更小的task,且采用非阻塞的執行方式;
事件驅動:
每個task對應一個特定的事件,當task準備就緒時,對應的事件通知就會發出。Reactor收到事件后,分發給綁定了對應事件的Handler執行task。
Reactor:負責響應事件,分發給綁定了該事件的handler執行task
Handler:綁定了某類事件,負責執行該事件對應的task。
Acceptor:Handler的一種,綁定了connect事件。它在系統啟動的時候就已經綁定了connnect事件,并注冊到reactor中。當有客戶端發起connect請求時,reactor會收到accept事件,然后分發給acceptor。acceptor首先會accept新的連接,然后新建一個handler,將其與該連接上得read事件綁定,并注冊到reactor中。