原文連接:http://www.javashuo.com/article/p-gpkwzcyn-er.htmlhtml
常規的誤區
假設有一個展現用戶詳情的需求,分兩步,先調用一個HTTP接口拿到詳情數據,而後使用適合的視圖展現詳情數據。
若是網速很慢,代碼發起一個HTTP請求後,就卡住不動了,直到十幾秒後纔拿到HTTP響應,而後繼續往下執行。
這個時候你問別人,剛剛代碼發起的這個請求是否是一個同步請求,對方必定回答是。這是對的,它確實是。
但你要問它爲何是呢?對方必定是這樣回答的,「由於發起請求後,代碼就卡住不動了,直到拿到響應後才能夠繼續往下執行」。
我相信不少人也都是這樣認爲的,其實這是不對的,是把因果關係搞反了:
不是由於代碼卡住不動了才叫同步請求,而是由於它是同步請求因此代碼才卡住不動了。
至於爲何能卡住不動,這是由操做系統和CPU決定的:
由於內核空間裏的對應函數會卡住不動,形成用戶空間發起的系統調用卡住不動,繼而使程序裏的用戶代碼卡住不動了。
所以卡住不動了只是同步請求的一個反作用,並不能用它來定義同步請求,那該如何定義呢?
同步和異步
所謂同步,指的是協同步調。既然叫協同,因此至少要有2個以上的事物存在。協同的結果就是:
多個事物不能同時進行,必須一個一個的來,上一個事物結束後,下一個事物纔開始。
那當一個事物正在進行時,其它事物都在幹嗎呢?
嚴格來說這個並無要求,但通常都是處於一種「等待」的狀態,由於一般後面事物的正常進行都須要依賴前面事物的結果或前面事物正在使用的資源。
所以,能夠認爲,同步更但願關注的是從宏觀總體來看,多個事物是一種逐個逐個的串行化關係,絕對不會出現交叉的狀況。
因此,天然也不太會去關注某個瞬間某個具體事物是處於一個什麼狀態。
把這個理論應用的出神入化的非「排隊」莫屬。凡是在資源少需求多的場景下都會用到排隊。
好比排隊買火車票這件事:
其實售票大廳更在乎的是旅客一個一個的到窗口去買票,由於一次只能賣一張票。
即便你們一窩蜂的都圍上去,仍是一次只能賣一張票,何須呢?擠在一塊兒又不安全。
只是有些人素質太差,非要往上擠,售票大廳無可奈何,採用排隊這種形式來達到本身的目的,即一個一個的買票。
至於每一個旅客排隊時的狀態,是看手機呀仍是說話呀,根本不用去在乎。
安全
除了這種因爲資源致使的同步外,還存在一種因爲邏輯上的前後順序致使的同步。
好比,先更新代碼,而後再編譯,接着再打包。這些操做因爲後一步要使用上一步的結果,因此只能按照這種順序一個一個的執行。
關於同步還需知道兩個小的點:
一是範圍,並不須要在全局範圍內都去同步,只須要在某些關鍵的點執行同步便可。
好比食堂只有一個賣飯窗口,確定是同步的,一我的買完,下一我的再買。但吃飯的時候也是一我的吃完,下一我的纔開始吃嗎?固然不是啦。
二是粒度,並非只有大粒度的事物纔有同步,小粒度的事物也有同步。網絡
只不太小粒度的事物同步一般是自然支持的,而大粒度的事物同步每每須要手工處理。
好比兩個線程的同步就須要手工處理,但一個線程裏的兩個語句自然就是同步的。
所謂異步,就是步調各異。既然是各異,那就是都不相同。因此結果就是:
多個事物能夠你進行你的、我進行個人,誰都不用管誰,全部的事物都在同時進行中。異步
一言以蔽之,同步就是多個事物不能同時開工,異步就是多個事物能夠同時開工。函數
注:必定要去體會「多個事物」,多個線程是多個事物,多個方法是多個事物,多個語句是多個事物,多個CPU指令是多個事物。等等等等。
spa
阻塞和非阻塞操作系統
所謂阻塞,指的是阻礙堵塞。它的本意能夠理解爲因爲遇到了障礙而形成的動彈不得。線程
所謂非阻塞,天然是和阻塞相對,能夠理解爲因爲沒有遇到障礙而繼續暢通無阻。
對這兩個詞最好的詮釋就是,當今中國一大交通難題,堵車:
汽車能夠正常通行時,就是非阻塞。一旦堵上了,所有趴窩,一動不動,就是阻塞。htm
所以阻塞關注的是不能動,非阻塞關注的是能夠動。blog
不能動的結果就是隻能等待,能夠動的結果就是繼續前行。
所以和阻塞搭配的詞必定是等待,和非阻塞搭配的詞必定是進行。
回到程序裏,阻塞一樣意味着停下來等待,非阻塞代表能夠繼續向下執行。
阻塞和等待
等待只是阻塞的一個反作用而已,代表隨着時間的流逝,沒有任何有意義的事物發生或進行。
阻塞的真正含義是你關心的事物因爲某些緣由沒法繼續進行,所以讓你等待。但不必乾等,你能夠作一些其它無關的事物,由於這並不影響你對相關事物的等待。
在堵車時,你能夠乾等。也能夠玩手機、和別人聊天,或者打牌、甚至先去吃飯都行。由於這些事物並不影響你對堵車的等待。不過你的車必須呆在原地。
在計算機裏,是沒有人這麼靈活的,通常在阻塞時,選在乾等,由於這最容易實現,只須要掛起線程,讓出CPU便可。在條件知足時,會從新調度該線程。
兩兩組合
所謂同步/異步,關注的是能不能同時開工。
所謂阻塞/非阻塞,關注的是能不能動。
經過推理進行組合:
同步阻塞,不能同時開工,也不能動。只有一條小道,一次只能過一輛車,可悲的是還TMD的堵上了。
同步非阻塞,不能同時開工,但能夠動。只有一條小道,一次只能過一輛車,幸運的是能夠正常通行。
異步阻塞,能夠同時開工,但不能夠動。有多條路,每條路均可以跑車,可氣的是全都TMD的堵上了。
異步非阻塞,能夠工時開工,也能夠動。有多條路,每條路均可以跑車,很爽的是全均可以正常通行。
是否是很容易理解啊。其實它們的關注點是不一樣的,只要搞明白了這點,組合起來也不是事兒。
回到程序裏,把它們和線程關聯起來:
同步阻塞,至關於一個線程在等待。
同步非阻塞,至關於一個線程在正常運行。
異步阻塞,至關於多個線程都在等待。
異步非阻塞,至關於多個線程都在正常運行。
I/O
IO指的就是讀入/寫出數據的過程,和等待讀入/寫出數據的過程。一旦拿到數據後就變成了數據操做了,就不是IO了。
拿網絡IO來講,等待的過程就是數據從網絡到網卡再到內核空間。讀寫的過程就是內核空間和用戶空間的相互拷貝。
因此IO就包括兩個過程,一個是等待數據的過程,一個是讀寫(拷貝)數據的過程。並且還要明白,必定不能包括操做數據的過程。
阻塞IO和非阻塞IO
應用程序都是運行在用戶空間的,因此它們能操做的數據也都在用戶空間。按照這樣子來理解,只要數據沒有到達用戶空間,用戶線程就操做不了。
若是此時用戶線程已經參與,那它必定會被阻塞在IO上。這就是常說的阻塞IO。用戶線程被阻塞在等待數據上或拷貝數據上。
非阻塞IO就是用戶線程不參與以上兩個過程,即數據已經拷貝到用戶空間後,纔去通知用戶線程,一上來就能夠直接操做數據了。
用戶線程沒有由於IO的事情出現阻塞,這就是常說的非阻塞IO。
同步IO和同步阻塞IO
按照上文中對同步的理解,同步IO是指發起IO請求後,必須拿到IO的數據才能夠繼續執行。
按照程序的表現形式又分爲兩種:
在等待數據的過程當中,和拷貝數據的過程當中,線程都在阻塞,這就是同步阻塞IO。
在等待數據的過程當中,線程採用死循環式輪詢,在拷貝數據的過程當中,線程在阻塞,這其實仍是同步阻塞IO。
網上不少文章把第二種歸爲同步非阻塞IO,這確定是錯誤的,它必定是阻塞IO,由於拷貝數據的過程,線程是阻塞的。
嚴格來說,在IO的概念上,同步和非阻塞是不可能搭配的,由於它們是一對相悖的概念。
同步IO意味着必須拿到IO的數據,才能夠繼續執行。由於後續操做依賴IO數據,因此它必須是阻塞的。
非阻塞IO意味着發起IO請求後,能夠繼續往下執行。說明後續執行不依賴於IO數據,因此它確定不是同步的。
所以,在IO上,同步和非阻塞是互斥的,因此不存在同步非阻塞IO。但同步非阻塞是存在的,那不叫IO,叫操做數據了。
因此,同步IO必定是阻塞IO,同步IO也就是同步阻塞IO。
異步IO和異步阻塞/非阻塞IO
按照上文中對異步的理解,異步IO是指發起IO請求後,不用拿到IO的數據就能夠繼續執行。
用戶線程的繼續執行,和操做系統準備IO數據的過程是同時進行的,所以才叫作異步IO。
按照IO數據的兩個過程,又能夠分爲兩種:
在等待數據的過程當中,用戶線程繼續執行,在拷貝數據的過程當中,線程在阻塞,這就是異步阻塞IO。
在等待數據的過程當中,和拷貝數據的過程當中,用戶線程都在繼續執行,這就是異步非阻塞IO。
第一種狀況是,用戶線程沒有參與數據等待的過程,因此它是異步的。但用戶線程參與了數據拷貝的過程,因此它又是阻塞的。合起來就是異步阻塞IO。
第二種狀況是,用戶線程既沒有參與等待過程也沒有參與拷貝過程,因此它是異步的。當它接到通知時,數據已經準備好了,它沒有由於IO數據而阻塞過,因此它又是非阻塞的。合起來就是異步非阻塞IO。
PS:聰明的你或許發現了我沒有提多路複用IO,由於它值得專門撰文一篇。