並發編程中的幾個名詞概念


現在,高並發,高流量已成為行業的熱點,並且各種高並發的技術也是層出不窮,如論是官方文檔還是市面上的各種書籍,我們在閱讀的時候都會遇到一些專業名詞,理解這些專業名詞之后,才能更好的理解內容。

一、同步與異步

介紹:

同步和異步通常來形容一次方法調用。

解釋一:同步方法調用一旦開始,調用者必須等到方法的調用返回后,才能繼續后續的行為。異步方法調用更像一個消息傳遞,一旦開始,方法調用就會立即返回,調用者可以繼續后續的操作。

解釋二:同步就是指一個進程在執行某個請求的時候,若該請求需要一段時間才能返回信息,那么這個進程將會一直等待下去,直到收到返回信息才繼續執行下去。異步是指進程不需要一直等下去,而是繼續執行下面的操作,不管其他進程的狀態。當有消息返回時系統會通知進程進行處理,這樣可以提高執行的效率。

畫圖解釋:

現實場景

畢業季,需要寫畢業論文,中午到飯點了,我們需要補充能量吃飯,怎么解決呢?這時有兩個選擇:

1. 出門就餐:出門去食堂或者去快餐店就餐,這時你可能需要點餐,等師父烹飪,等服務員上菜,然后就餐,等你吃完之后你才可以回去繼續畢業論文的書寫。

2. 點賣外:通過外賣平台訂購外賣,在等外賣送來的過程中,你可以繼續書寫論文或做其他的事。

 出宿舍就餐就是一個同步方法的過程,兩件事的執行時間是順序連着的;點外賣就是一個異步的過程,兩件事的執行時間是並行異步的。

 二、並發和並行

介紹

解釋一:並行是指兩個或者多個事件在同一時刻發生;而並發是指兩個或多個事件在同一時間間隔發生。

解釋二:並行是在不同實體上的多個事件,並發是在同一實體上的多個事件

解釋三:在一台處理器上“同時”處理多個任務,在多台處理器上同時處理多個任務。如hadoop分布式集群

並發偏重於多個任務交替執行,而多個任務之間有可能還是串行的。而並行是真正意義上的“同時執行”。

嚴格意義上來說,並行的多個任務是真實的同時執行的,而對於並發來說,這個過程只是交替的,一會兒執行A,一會兒執行B,系統會不停的在兩者之間切換。但對於外部觀察者來說,即使多個任務之間是串行並發的,也會造成多任務間是並行執行的錯覺。

實際上,如果系統內只有一個CPU,而使用多進程或者多線程任務,那么真實環境中這些任務不可能是真實並行的,畢竟一個CPU一次只能執行一條指令,這種情況下多進程或者多線程就是並發,而不是並行(操作系統會不停切換多個任務)。真實的並行也只可能出現在擁有多個CPU的系統中(比如多核CPU)。

畫圖解釋:

現實場景

爬山,國內一般在相對較高的名山,為照顧游客都會提供兩種方式登山:一種是游客自己憑借雙腿,另一種就是借助纜車。

1. 游客自己憑借雙腿登山:游客自己憑雙腿登山的時候,一般都是走走停停,一是為了歇息,還有就是停下來看風景,因為不停下來,是沒有辦法專心看風景的,登山的過程中,需要注意腳下、注意安全。登山和看風景是交替並發執行的。這就是一個並發的過程。

2. 借助纜車:游客花點錢,坐纜車游玩登山,這時我們可以不必走走停停,也不需要關注腳下。我們可以一邊往山頂移動,一邊欣賞沿途的風景。登山和看風景是兩件同時進行的事情。這就是一個並行的過程。

三、臨界區

介紹

臨界區用來表示一種公共資源或者說是共享數據,可以被多個線程使用。但是每一次只能有一個線程使用它,一旦臨界區資源被占用,其他線程要想使用這個資源,就必須等待。

畫圖解釋

 

現實場景

打印機,我們工作的時候肯定會經常用到打印機,打印機就是一個臨界區的最好例子。一台打印機一次只能執行一個任務,如果 A 和 B 同時需要打印文件,很顯然,如果 A 先發下打印的任務,打印機就開始打印 A 的文件。B 的任務就只能等待 A 打印結束之后才能打印。

四、阻塞和非阻塞

阻塞和非阻塞通常形容多線程之間的相互影響。比如一個線程占用了臨界區資源,那么其他所有需要這個資源的線程就必須在這個臨界區中進行等待。等待會導致線程掛起,這種情況就是阻塞。此時,如果占用資源的線程一直不願意釋放資源,那么其他所有阻塞在這個臨界區上的線程都不能工作。

當我們使用synchronized關鍵字,或者重入鎖時(后續會有文章詳細的介紹Java中鎖的分類),我們得到的就是阻塞的線程。無論是synchronized或者重入鎖,都會試圖在執行后續代碼前,得到臨界區的鎖,如果得不到,線程就會被掛起等待,直到占有了所需資源為止。

非阻塞允許多個線程同時進入臨界區 。

五、死鎖,飢餓,活鎖

死鎖,飢餓和活鎖都屬於多線程的活躍性問題。如果發現上述幾種情況,那么相關線程可能就不再活躍,也就說它可能很難再繼續往下執行了。

死鎖:所謂死鎖,是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。

例如,如果線程1鎖住了A,然后嘗試對B進行加鎖,同時線程2已經鎖住了B,接着嘗試對A進行加鎖,這時死鎖就發生了。線程1永遠得不到B,線程2也永遠得不到A,並且它們永遠也不會知道發生了這樣的事情。為了得到彼此的對象(A和B),它們將永遠阻塞下去。這種情況就是一個死鎖。

借助《實戰Java高並發程序設計》中的圖:

A、B、C、D四輛小車在這種情況下都無法繼續行駛了。他們彼此之間相互占用了其他車輛的車道,如果大家都不願意釋放自己的車道,那么這個狀態將永遠的維持下去,誰都不可能通過。

飢餓: 飢餓是指某一個或者多個線程因為種種原因無法獲得所需要的資源,導致一直無法執行。 

產生這種情況的原因是多種的,可能是它的線程優先級太低,而高優先級的線程不搶占它需要的資源,導致低優先級線程無法工作。也可能是某個線程一直占着關鍵資源不放,導致其他需要這個資源的線程無法正常執行。

活鎖:活鎖指的是任務或者執行者沒有被阻塞,由於某些條件沒有滿足,導致一直重復嘗試,失敗,嘗試,失敗。

活鎖可以認為是一種特殊的飢餓。

列舉現實生活中的一個例子:一條狹窄的道路,A 和 B 迎面相遇了,緣分使然,A紳士的想讓出道路讓B先過,這時B也保持良好的淑女形象想讓出道路讓A先過,導致兩人都避讓了;兩人尷尬一笑,之后A想着B既然讓了,就准備先過,巧的是,這時B心里也想着,A既然讓了,不如先過,兩人又撞上了;然后又開始禮貌性的相互避讓,避讓之后各自又想先走結果又撞上了,結果兩人都沒過去。這種情況就是活鎖。

線程都秉承着"謙讓"的原則,主動將資源釋放給他人使用,那么就會出現資源不斷在兩個線程之間跳動,而沒有一個線程可以同時拿到所有資源而正常執行。

 

參考書籍:《實戰Java高並發程序設計》,《Java並發編程的藝術》

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM