實戰Java高併發程序設計-02幾個重要概念

同步與異步

概念

同步和異步通常用來形容一次方法調用。同步方法調用一旦開始,調用者必須等到方法調用完成並返回後,才能繼續後續的工作。異步方法調用更像一個消息傳遞,被調用之後,調用者馬上開始後續工作。而異步方法通常在另外一個線程中“真實”地執行。整個過程不會阻礙調用者的工作,如果異步調用需要返回結果,那麼當這個異步調用真實完成時,則會通知調用者。

舉例說明

同步: 去超市買食物,通常是選購食物,結賬,拿回家,到了這裏纔算完成了購物。在購物期間你無法再家裏看電視,吹空調。

異步: 打開電腦,通過網絡超市訂購食物,當你網上支付完成。這個時候購物就已經完成,後面只需要等待快遞員送貨上門即可。在收到貨的期間,可以看電視,吹空調。

併發與並行

概念

並行:真實的多個任務同時進行
併發:一會執行A任務,一會兒執行B任務,來回切換的速度非常快,導致錯覺認爲任務是並行執行。

舉例說明

併發:從A到B地去,開車過去。 那麼開車的過程中不允許打電話,打電話就不讓開車。如果要打電話,就要停車,打完電話在走。如果要開車就必須結束通話才能開車。這樣反覆輪詢 開車-要打電話-停車-打電話-結束通話-開車-要打電話-停車-打電話-結束通話-開車….
並行:從A到B地,坐車過去,那麼在接近目的地的同時還可以自由的打電話,而不用停車。

臨界區

概念

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

舉例說明

辦公司有一臺打印機,打印機一次只能執行一個任務,如果A和B兩個人同時需要打印文件,很顯然,如果A先下發了打印任務,那麼B就只能等待A打印完成。如果一會打印A,一會打印B,最終打印出來的文件既不是A所需要的也不是B所需要的。

阻塞(Blocking)和非阻塞(Non-Blocking)

概念

阻塞:當多個線程同時訪問一個臨界區資源的時候,只能排隊,一個線程處理完畢之後,後面的線程就必須等待正在訪問的線程執行完畢。這種情況就叫做阻塞
非阻塞:當多個線程訪問同一個資源的時候,不設置任何限制,多個線程可以同時處理。

舉例說明

阻塞:在跑步機上跑步,一臺跑步機上面同時只能有一個人進行跑步,如果後面的人要進行跑步,必須等待正在跑步的人下來。
非阻塞:看展覽,在一個展覽上所展示的物品,可以同時供多個圍觀羣衆欣賞。

死鎖、活鎖、飢餓

概念

死鎖:當兩個線程同時都需要資源A和B 才能進行下一步的工作,但是線程a拿到了A,線程b拿到了資源B,兩個線程互不釋放,最後造成線程a無法正常工作,線程b也無法正常工作
活鎖:當兩個線程同時都需要資源A和B 才能進行下一步的工作,但是線程a拿到了A,線程b拿到了資源B。
這時候a發現無法完成任務,就釋放了手裏的自由A,線程b也發現無法完成任務,也釋放了手裏的資源B,這時候a線程拿到了資源B,b線程拿到了資源A,又無法滿足條件進行下一步。又繼續各自釋放手裏的資源,如此循環。
飢餓: 當一個線程的優先級非常的低,每次都無法執行,一直被高優先級的線程執行,遲遲不能執行。

舉例說明

死鎖:開一把鎖需要同時需要兩把鑰匙,兩個人各自拿了其中一把,都不放開手裏的鑰匙,這樣永遠無法完成打開鎖。
活鎖:電梯出來一個人,一個人進電梯。 出來的人 往走,進去的也往左。這時候出來的往右,進去的也往右,不斷循環。。。。最終出去的無法離開,進去的無法進去。
飢餓:鳥媽媽餵食物給小鳥吃,有一個小鳥弱小,每次鳥媽媽抓回來的蟲子都被其他的小鳥吃掉,這個弱小的鳥每次都吃不到蟲子。

併發級別

由於臨近區的存在,多線程之間的併發必須受到控制,根據併發的策略,可以把併發級別進行分類,大致上可以分爲阻塞、無飢餓、無障礙,無等待等幾種.

阻塞(blocking)

一個線程是阻塞的,那麼在其他線程釋放資源之前,當前線程無法繼續執行。當使用synchronize關鍵字,或者重入鎖,用的就是阻塞線程.
無論是synchronize或者重入鎖,都會試圖在執行後續代碼前,得到臨近區的鎖,如果得不到,線程會被掛起等待,直到佔了所需要的資源爲止.

無飢餓

如果線程之間存在優先級,那麼線程調度的時候總是傾向於滿足高優先級的線程,也就是說,對於同一個資源的分配,這是不公平的,就像排隊一樣,不斷有人插隊,那麼後面的人永遠無法排到前面去

無障礙

無障礙是一種最弱的非阻塞調度。兩個線程如果無障礙的執行,那麼他們不會因爲臨界區的問題一方線程被掛起。
但是當兩個線程都進入了臨界區,一起把數據改壞了,那麼無障礙線程一旦檢查到了這種狀況,它就會立即對自己所做的修改進行回滾,確保數據的安全。但是如果沒有數據資源的競爭發生,那麼線程就會順利完成工作,然後走出臨界區。

無鎖(非阻塞同步)

無鎖的並行都是無障礙的,在無鎖的情況下,所有的線程都嘗試對臨界區進行訪問,但是不同的是,無鎖的併發保證必然有一個線程能夠在有限的次數內完成操作,離開臨界區。

無等待

無鎖只需要有一個線程可以在有限的次數內完成操作,而無等待則是在無鎖的基礎上更進一步進行擴展。它要求所有的線程都必須在有限次數內完成,這樣就不會產生飢餓問題。如果限制這個步驟上限,還可以進一步分解爲有界無等待和線程無關的無等待幾種,它們之間的區別只是對循環次數的限制不同。

基本思想是,對數據的讀取可以不加以任何控制,因此所有的讀取線程都是無等待的,它們既不會被鎖定等待,也不會引起任何的衝突。但是在寫操作的時候,先取得原始數據的副本,接着只修改副本數據,修改完成後,在合適的時機回寫數據

Java內存模型(JMM)

上面所描述的概念,是使用任何一門語言編寫併發程序都會涉及到的問題。
在Java內存模型中主要圍繞着 原子性、有序性、可見性來建立的。

原子性

原子性是指一個操作是不可間斷,即多個線程開始執行的時候,一旦操作開始執行,就不會被其他線程干擾.

cas算法 示例:
在cas算法中包含三個參數 V,E,N 。 V表示要更新的變量,E表示預期值,N表示新值。僅當V等於E時,纔會將V設置爲N,如果V不等於E,說明其他線程做了更新,則當前線程什麼都不做。最後cas返回當前V的真實值。CAS操作是抱着樂觀的態度進行的,它總是認爲自己的操作可以成功。當多個線程同時操作一個變量時,只會有一個會操作成功,其他的都會失敗。
而原子操作則是在cas的基礎上,寫入一個while(true) 在循環中,操作成功的線程就會跳出,而失敗的線程在循環裏面繼續執行,直到成功操作爲止。

可見性

可見性是指當一個線程修改了某一個共享變量的值,其他線程是否能夠立即知道這個修改。

有序性

有序性是指多個線程的操作在併發時,執行先後問題.

發佈了87 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章