Java併發小結01

zxxstar發表於2023-03-14

Java併發小結01

主要參考自《實戰Java高併發程式設計》。

需要知道的概念

- 同步與非同步
- 併發與並行
- 臨界區
- 阻塞與非阻塞
- 死鎖、飢餓、活鎖
同步與非同步
同步:同步方法一旦被呼叫,必須等待方法返回後才能繼續後續的行為。
非同步:非同步方法就像一個訊息傳遞,被呼叫後方法會立即返回,呼叫者可以開始後續的行為。
併發與並行
並行:兩個任務同時執行。
併發:一段時間內,多個任務在CPU交替執行,看似並行。
臨界區
用來表示一種可以被多個執行緒使用的公共資源,但是一次只能一個執行緒使用。一旦臨界區被佔用,其他執行緒只能等待。
比如說印表機:一次只能列印一份檔案,要是交替列印,那麼列印出來的東西是不可用的。
阻塞與非阻塞
阻塞:一個執行緒佔用了臨界區資源,其他執行緒需要這個資源就得等待,等待會導致執行緒掛起,這就是阻塞。
非阻塞:與阻塞相反,沒有一個執行緒可以導致其他執行緒阻塞,所有執行緒都不斷嘗試繼續執行。
死鎖、飢餓、活鎖
死鎖:兩個或兩個以上執行緒相互請求其他執行緒的資源,誰都執行不下去。
飢餓:一個執行緒因為種種原因一直獲取不到需要的資源導致無法執行。
活鎖:執行緒之間將資源相互推讓而沒有一個執行緒拿到資源繼續執行。

併發級別

- 阻塞
- 無飢餓
- 無障礙
- 無鎖
- 無等待
阻塞

使用synchronized關鍵字或重入鎖,得到的就是阻塞的執行緒。

無飢餓

執行緒預設是不公平的(理論上優先滿足優先順序高的),會導致飢餓,公平鎖解決飢餓問題。

無障礙

無障礙是一種最弱的非阻塞排程。兩個執行緒如果無障礙地執行,那麼不會因為臨界區的問題導致一方被掛起。如果資料壞了就回滾,沒有資料競爭就順利完成工作,走出臨界區。
無障礙有可能會因為資料衝突一直回滾,一種可行的無障礙實現可以依賴一個“一致性標記”來實現。

無鎖

無鎖的並行都是無障礙的。無鎖的狀態下,所有的執行緒都能嘗試對臨界區進行訪問,不同的是,無鎖的併發保證必然有一個執行緒能夠在有限步內完成操作離開臨界區。
會出現執行緒飢餓。
無鎖的特點:可能會包含一個無窮迴圈。在這個迴圈中,執行緒會不斷地嘗試修改共享變數。如果沒有衝突,修改成功,走人,否則繼續嘗試。

無等待

無鎖只要求有一個執行緒在有限步內完成操作,而無等待則在無鎖的基礎上更近一步擴充套件。它要求所有執行緒都必須在有限步數內完成,這樣就不會引起飢餓問題。

JMM

探討一下java記憶體模型:原子性、可見性、有序性。

原子性:

一個操作是不可中斷的。

可見性

一個執行緒修改了某個共享變數的值時,其他執行緒會立馬知道這個修改。

有序性

程式在執行時,可能就進行指令排序,排序後的指令順序與原指令順序未必一致。
但是指令排序可以保證序列語義一致,不保證並行語義一致。

那些指令不能排序:Happen-Before原則
  • 程式順序原則:一個執行緒內保證語義的序列性。

  • volatile 原則:volatile變數的寫先於讀發生,這保證了volatile變數的可見性。

  • 鎖規則:解鎖(unlock)必然發生在隨後的加鎖(lock)前面。

  • 傳遞性:A先於B,B先於C,那麼A先於C。

  • 執行緒的start()方法先於它的每一個動作。

  • 執行緒的中斷(interrupt)先於被中斷執行緒的程式碼。

  • 物件的建構函式的執行、結束先於finalize()方法。

個人小結:

這些都是概念性問題,如果第一次不太熟悉可以baidu一下進行理解。

作為《實戰Java高併發程式設計》的一章,主要介紹了併發的一些概念性關鍵詞,建議深入理解,後面的多執行緒併發操作都建立在這些概念之上。

其中第一章有一個小結介紹有關並行的兩個定律我忽略了,感興趣的可以自己去看: Amdahl定律和Gustafson定律。

相關文章