Java—5 內建鎖優化

Java—5 內建鎖優化

所謂內建鎖的優化就是優化線程的等待時間

CAS機制

1.CAS:全稱 Compare And Swap 比較交換機制

CAS是一種樂觀鎖機制:

悲觀鎖:在任意時刻都有線程競爭鎖,獲取鎖成功的線程會阻塞獲取鎖失敗的線程成功獲取鎖。

樂觀鎖(lock):假設所有線程訪問共享資源均不會發生衝突,既然不會發生衝突,就不會阻塞其他線程。不會阻塞線程

CAS(無鎖操作):有三個值V(內存實際存放的值),O(預期值),N更新後的值。

當V==O,說明上次該線程操作後沒有別的線程再修改值,因此可以將N存入V中;

當V!=O,說明上次該線程操作後,有其他線程修改了V值,因此不能講N替換到內存中,返回V值,並且不斷嘗試修改V值。

JDK1.6之前的內建鎖最大的問題是,每當有線程競爭鎖失敗就會將線程阻塞,這樣阻塞和喚起線程消耗了大量的資源。

CAS不是武斷的將競爭鎖失敗的線程掛起來,而是會讓失敗的線程自旋嘗試獲取鎖,到了一定次數再將線程掛起。

具體策略:如果上次自旋獲取到鎖,這次 自旋時間就長一些,否則,此次自旋時間就短一些。

CAS的問題

ABA問題:線程1 A 線程2 A—>B 線程3 B—>A 線程1 : ???

理論上來說 線程1是可以修改的,但是如果修改了,線程3就會癱瘓。

解決思路:數據庫的樂觀鎖機制,加入一個版本號: 包java.util.concurrent.atomic的 AtomicReference類

公平性問題:處於自旋狀態的線程會比阻塞狀態的線程更容易獲取鎖。

在同步代碼塊中,獲得對象monitor監視器就是獲得了對象的鎖。無非就是對象的鎖就是對象頭裏的一個標記

偏向鎖

HotSpot的作者發現,在大多數情況下,線程時不存在競爭的,爲了降低 阻塞和喚醒線程的代價引入了偏向鎖

在任意時刻只有一個線程持有鎖。

偏向鎖的獲取過程

1.一個線程訪問同步代碼塊並獲取鎖時時,會在對象頭和棧幀中存儲鎖偏向的線程ID,先看對象頭持有的線程ID是否爲自己的線程ID,如果是直接訪問同步代碼塊。

2.如果不是,查看偏向鎖字段,如果爲0,說明對象鎖還沒有被任何線程獲得,將偏向鎖字段改爲1,並且將對象頭的線程ID改成自己的。

3,如果爲1,說明對象鎖已經被別的線程獲得,當前線程不斷自旋獲取對象鎖或升級成輕量級鎖。(後者可能性較大)。一般來說 偏向鎖每自旋一次 mark word中的 Epoch就加1 當 Epoch=40時,說明該鎖已經不適合當做偏向鎖。升級爲輕量級鎖

偏向鎖的釋放:

偏向鎖的釋放代價較大,通常要到達全局安全點(CPU上沒有執行有用字節碼)纔可以釋放
在這裏插入圖片描述

偏向鎖鎖競爭,鎖升級

在這裏插入圖片描述

輕量級鎖

在不同時間,多個線程請求一把鎖。

加鎖過程:

1.當線程訪問同步代碼塊並加鎖時,JVM會在線程中開闢存放鎖記錄的棧幀,將對象頭的mark word複製到鎖記錄中 稱爲 displace mark word。

2.使用CAS將對象頭中的Mark word 替換成指向鎖記錄的指針。

3.如果成功說明,當前成功獲取鎖,如果失敗說明當前有其他線程競爭鎖,不斷自旋嘗試獲取鎖。

解鎖:

1.將鎖記錄中的 Displace Mark Word替換會 Mark Word 。

2.如果成功,成功釋放鎖,如果失敗,說明有其他線程競爭鎖,鎖直接膨脹成重量級鎖
在這裏插入圖片描述

輕量級鎖競爭(一存在競爭就升級)

在這裏插入圖片描述

總結

**重量級鎖:**採用自適應自旋來獲取鎖,避免了對於極小的同步代碼塊還存在阻塞問題。同一時間多個線程競爭鎖。
**輕量級鎖:**採用CAS獲取鎖,將鎖對象替換成指針,指向當前線程棧上的一塊空間,存儲着鎖對象原本的標記字段。它針對的是多個線程在不同時間申請一把鎖。
**偏向鎖:**偏向鎖只會在第一次請求時採用CAS操作。在鎖對象的標記中記錄下當前線程的地址。在之後的運行中只要簡單比較一下對象頭是否持有線程ID,如果測試成功,就會直接引用同步代碼塊。針對鎖僅會被同一塊線程持有。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章