一:鎖的原理結構
(1)鎖對象內部維護了一個同步管理器的對象AbstractQueuedSynchronizer,AbstractOwnableSynchronizer
(2)該對象其實是一個抽象類,具體的鎖的管理器繼承該抽象類
(3)該抽象類的關鍵屬性有:---->Thread exclusiveOwnerThread(獲取鎖的線程對象)
----> Node head(首節點,正在擁有當前鎖的線程構造的Node對象)
---->Node tail(尾巴節點,等待獲取鎖的線程構造的雙向鏈表結構的隊列的最后一個節點)
---->int state(當前該鎖被線程爭搶的狀態,如果state=0,表示無線程爭搶該鎖,如果state>0則表示已經有線程擁有該鎖)
二:鎖中的Node隊列的結構
(1)所有的線程在執行到獲取鎖的代碼的部分,都會調用同步管理器的lock()方法,如果有線程獲取鎖,則將該線程構造成node對象,添加到隊列尾部,並調用系統命令阻塞當前線程。也就是代碼執行到這,當前線程就不再執行。
(2)擁有鎖的線程,在釋放鎖的時候,會喚醒自己的后繼節點的線程,讓其爭搶鎖。
(3)Node的內部結構屬性
--->int waitStatus(當前node的線程在爭搶鎖過程的狀態標識)
--->Node prev(當前node的上一個node的引用,前驅節點)
--->Node next(當前node的下一個INITALnode的引用,后繼節點)
--->Thread thread(當前node所代表的線程的線程對象)
--->Node nextWaiter(下一個等待着的node)
(4)node等待狀態的的含義
CANCELLED = 1:由於在同步隊列中等待的線程等待超時或被中斷,需要從同步隊列中取消等待,節點進入該狀態不會再變化
SIGNAL = -1:后繼節點線程處於等待狀態,而當前節點的線程如果釋放了同步狀態或者被取消,將會通知后繼節點,使后繼節點得以運行。
CONDITION = -2:在等待隊列中,節點線程等待在Condition上,當其他線程對Condition調用了signal()方法后,該節點將會從等待隊列中轉移到同步隊列中。加入到對同步狀態的獲取中。
PROPAGATE = -3:表示下一次共享式同步狀態獲取將會無條件被傳播下去。
INITIAL=0:初始化狀態
三:公平鎖和非公平鎖的區別
(1)公平鎖的鎖獲取,嚴格按照等待順序進行鎖獲取,在獲取鎖的時候有一個判斷hasQueuedPredeccssors(),同步隊列中是否有前驅節點在等待獲取鎖,如果有,則放棄獲取鎖,而是添加到隊尾,排隊獲取鎖
(2)非公平鎖,是獲取鎖的順序是隨機的,甚至,有的線程可能會一直無法獲取鎖,出現線程飢餓情況。
(3)公平鎖的性能往往沒有非公平鎖的性能高,因為它需要排隊,則需要進行程序狀態的切換,要比非公平鎖的切換次數多。
(4)公平鎖:在獲取鎖的時候,先檢查是否已經有隊列形成。如果有,則加入隊列。按順序排隊,獲取鎖。
(5)非公平鎖:在獲取鎖的時候,會多次嘗試獲取鎖,而不着急加入隊列排隊,更有主動權獲取鎖。如果多次獲取也沒獲取成功,也加入排隊,此時和公平鎖一樣。其兩者的區別在於:非公平鎖,加大了新晉線程搶到鎖的概率。
四:鎖的獲取和釋放的過程圖
【LockSupport】工具的api
park():阻塞當前線程,如果調用unpark(Thread thread)方法或者當前線程被中斷,才能從park()方法返回。
parkNanos(long nanos):阻塞當前線程,最長不超過nanos納秒,返回條件在park()的基礎上增加了超時返回。
parkUntil(long deadline):阻塞當前線程,直到deadline時間(從1970年開始到deadline時間的毫秒數)
unpark(Thread thread):喚醒處於阻塞狀態的線程thread
park(Object blocker):阻塞當前線程,如果調用unpark(Thread thread)方法或者當前線程被中斷,才能從park(Object blocker)方法返回。blocker用於問題排查和系統監控。
parkNanos(Object blocker,long nanos):阻塞當前線程,最長不超過nanos納秒,返回條件在park(Object blocker)的基礎上增加了超時返回。blocker用於問題排查和系統監控。
parkUntil(Object blocker,long deadline):阻塞當前線程,直到deadline時間(從1970年開始到deadline時間的毫秒數),blocker用於問題排查和系統監控。
======================公平鎖和非公平鎖=================
轉載:全文地址請點擊:https://blog.csdn.net/qyp199312/article/details/70598480?utm_source=copy
公平鎖:
- 公平和非公平鎖的隊列都基於鎖內部維護的一個雙向鏈表,表結點Node的值就是每一個請求當前鎖的線程。公平鎖則在於每次都是依次從隊首取值。
- 鎖的實現方式是基於如下幾點:
- 表結點
Node
和狀態state
的volatile
關鍵字。 sum.misc.Unsafe.compareAndSet
的原子操作(見附錄)。
- 表結點
非公平鎖:
- 在等待鎖的過程中, 如果有任意新的線程妄圖獲取鎖,都是有很大的幾率直接獲取到鎖的。
ReentrantLock
鎖都不會使得線程中斷,除非開發者自己設置了中斷位。
ReentrantLock
獲取鎖里面有看似自旋的代碼,但是它不是自旋鎖。
ReentrantLock
公平與非公平鎖都是屬於排它鎖。
公平鎖和非公平鎖
ReentrantLock
的公平鎖和非公平鎖都委托了 AbstractQueuedSynchronizer#acquire
去請求獲取。
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
tryAcquire
是一個抽象方法,是公平與非公平的實現原理所在。addWaiter
是將當前線程結點加入等待隊列之中。公平鎖在鎖釋放后會嚴格按照等到隊列去取后續值,而非公平鎖在對於新晉線程有很大優勢。acquireQueued
在多次循環中嘗試獲取到鎖或者將當前線程阻塞。selfInterrupt
如果線程在阻塞期間發生了中斷,調用Thread.currentThread().interrupt()
中斷當前線程。
ReentrantLock
對線程的阻塞是基於LockSupport.park(this);
(見AbstractQueuedSynchronizer#parkAndCheckInterrupt
)。 先決條件是當前節點有限次嘗試獲取鎖失敗。
公平鎖和非公平鎖在說的獲取上都使用到了 volatile
關鍵字修飾的state
字段, 這是保證多線程環境下鎖的獲取與否的核心。
但是當並發情況下多個線程都讀取到 state == 0
時,則必須用到CAS技術,一門CPU的原子鎖技術,可通過CPU對共享變量加鎖的形式,實現數據變更的原子操作。
volatile 和 CAS的結合是並發搶占的關鍵。
CAS和volatile, Java並發的基石
volatile 是Java語言的關鍵字, 功能是保證被修飾的元素(共享變量):
任何進程在讀取的時候,都會清空本進程里面持有的共享變量的值,強制從主存里面獲取;
任何進程在寫入完畢的時候,都會強制將共享變量的值寫會主存。
volatile 會干預指令重排。
volatile 實現了JMM規范的 happen-before 原則。
在多核多線程CPU環境下, CPU為了提升指令執行速度,在保證程序語義正確的前提下,允許編譯器對指令進行重排序。也就是說這種指令重排序對於上層代碼是感知不到的,我們稱之為 processor ordering.
JMM 允許編譯器在指令重排上自由發揮,除非程序員通過 volatile等 顯式干預這種重排機制,建立起同步機制,保證多線程代碼正確運行。見文章:Java並發:volatile內存可見性和指令重排。
當多個線程之間有互相的數據依賴的之后, 就必須顯式的干預這個指令重排機制。
CAS是CPU提供的一門技術。在單核單線程處理器上,所有的指令允許都是順序操作;但是在多核多線程處理器上,多線程訪問同一個共享變量的時候,可能存在並發問題。
使用CAS技術可以鎖定住元素的值。Intel開發文檔, 第八章
編譯器在將線程持有的值與被鎖定的值進行比較,相同則更新為更新的值。
CAS同樣遵循JMM規范的 happen-before 原則。
看JAVA CAS原理深度分析博客
公平鎖和非公平鎖在說的獲取上都使用到了 volatile
關鍵字修飾的state
字段, 這是保證多線程環境下鎖的獲取與否的核心。
但是當並發情況下多個線程都讀取到 state == 0
時,則必須用到CAS技術,一門CPU的原子鎖技術,可通過CPU對共享變量加鎖的形式,實現數據變更的原子操作。
volatile 和 CAS的結合是並發搶占的關鍵。
--------------------- 本文來自 平菇蝦餃 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/qyp199312/article/details/70598480