樂觀鎖與悲觀鎖對比

在這裏插入圖片描述

樂觀鎖

CAS(compare and swap):先比較再替換是樂觀鎖的一種實現思想,樂觀鎖是一種輕量級的鎖。

循環等待問題:如果CAS操作長時間不成功的話,就會一直自旋,佔用CPU。

ABA問題:
線程1讀取了數據A
線程2讀取了數據A
線程2通過CAS比較,發現值是A沒錯,可以把數據A改成數據B
線程3讀取了數據B
線程3通過CAS比較,發現數據是B沒錯,可以把數據B改成了數據A
線程1通過CAS比較,發現數據還是A沒變,就寫成了自己要改的值。

ABA問題解決方法:
加標誌位,例如搞個自增的字段,操作一次就自增加一,或者搞個時間戳,比較時間戳的值。

只能保證單個元素的原子性
CAS操作單個共享變量的時候可以保證原子的操作,多個變量就不行了,JDK 5之後 AtomicReference可以用來保證對象之間的原子性,就可以把多個對象放入CAS中操作。

一般數據庫查詢,訂單表等高併發的地方使用樂觀鎖比較多。

悲觀鎖

synchronized加鎖,synchronized 是最常用的線程同步手段之一,上面提到的CAS是樂觀鎖的實現,synchronized就是悲觀鎖了。

synchronized 對對象進行加鎖,在 JVM 中,對象在內存中分爲三塊區域:對象頭(Header)、實例數據(Instance
Data)和對齊填充(Padding)。

對象頭:我們以Hotspot虛擬機爲例,Hotspot的對象頭主要包括兩部分數據:Mark Word(標記字段)、Klass Pointer(類型指針)。

Mark Word:默認存儲對象的HashCode,分代年齡和鎖標誌位信息。它會根據對象的狀態複用自己的存儲空間,也就是說在運行期間Mark Word裏存儲的數據會隨着鎖標誌位的變化而變化。
Klass Point:對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。
在這裏插入圖片描述
在這裏插入圖片描述
當 Monitor 被某個線程持有後,就會處於鎖定狀態,如圖中的 Owner 部分,會指向持有 Monitor 對象的線程。

另外 Monitor 中還有兩個隊列分別是EntryList和WaitList,主要是用來存放進入及等待獲取鎖的線程。

如果線程進入,則得到當前對象鎖,那麼別的線程在該類所有對象上的任何操作都不能進行。

synchronized應用在對象上:

每個對象都會與一個monitor相關聯,當某個monitor被擁有之後就會被鎖住,當線程執行到monitorenter指令時,就會去嘗試獲得對應的monitor。

步驟如下:

每個monitor維護着一個記錄着擁有次數的計數器。未被擁有的monitor的該計數器爲0,當一個線程獲得monitor(執行monitorenter)後,該計數器自增變爲 1 。
當同一個線程再次獲得該monitor的時候,計數器再次自增;
當不同線程想要獲得該monitor的時候,就會被阻塞。
當同一個線程釋放 monitor(執行monitorexit指令)的時候,計數器再自減。

當計數器爲0的時候,monitor將被釋放,其他線程便可以獲得monitor。

synchronized應用在方法上

同步方式是通過方法中的access_flags中設置ACC_SYNCHRONIZED標誌來實現

synchronized應用代碼塊上

同步代碼塊是通過monitorenter和monitorexit來實現。

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