摘要:二什么是重入鎖可重入鎖,顧名思義,支持重新進(jìn)入的鎖,其表示該鎖能支持一個(gè)線程對(duì)資源的重復(fù)加鎖。將由最近成功獲得鎖,并且還沒(méi)有釋放該鎖的線程所擁有。可以使用和方法來(lái)檢查此情況是否發(fā)生。
一、寫(xiě)在前面
前幾篇我們具體的聊了AQS原理以及底層源碼的實(shí)現(xiàn),具體參見(jiàn)
《J.U.C|一文搞懂AQS》
《J.U.C|同步隊(duì)列(CLH)》
《J.U.C|AQS獨(dú)占式源碼分析》
《J.U.C|AQS共享式源碼分析》
本章我們來(lái)聊一聊其實(shí)現(xiàn)之一 可重入鎖ReentrantLock的實(shí)現(xiàn)原理以及源碼分析。
注 :本章主要講解非公平鎖的實(shí)現(xiàn)流程和源碼解析,其中涉及到AQS底層的實(shí)現(xiàn)因在前面幾章都已經(jīng)詳細(xì)聊過(guò)在這會(huì)一筆帶過(guò)。
二、什么是重入鎖可重入鎖 ReentrantLock ,顧名思義,支持重新進(jìn)入的鎖,其表示該鎖能支持一個(gè)線程對(duì)資源的重復(fù)加鎖。
Java API 描述
一個(gè)可重入的互斥鎖 Lock,它具有與使用 synchronized 方法和語(yǔ)句所訪問(wèn)的隱式監(jiān)視器鎖相同的一些基本行為和語(yǔ)義,但功能更強(qiáng)大。ReentrantLock 將由最近成功獲得鎖,并且還沒(méi)有釋放該鎖的線程所擁有。當(dāng)鎖沒(méi)有被另一個(gè)線程所擁有時(shí),調(diào)用 lock 的線程將成功獲取該鎖并返回。如果當(dāng)前線程已經(jīng)擁有該鎖,此方法將立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法來(lái)檢查此情況是否發(fā)生。
ReentrantLock還提供了公平鎖和非公平鎖的選擇, 其構(gòu)造方法接受一個(gè)公平參數(shù)(默認(rèn)是非公平方式),當(dāng)傳入ture時(shí)表示公平鎖, 否則為非公平鎖。其兩者的主要區(qū)別在于公平鎖獲取鎖是有順序的。但是其效率往往沒(méi)有非公平鎖的效率高,在多線程的訪問(wèn)時(shí)往往表現(xiàn)很低的吞吐量(即速度慢,常常急慢)。
來(lái)張圖緩解下
我們先來(lái)看一段代碼
ReentrantLock lock = new ReentrantLock(); try { lock.lock(); // 業(yè)務(wù)代碼 } finally { lock.unlock(); }
這一段代碼相信學(xué)過(guò)Java的同學(xué)都非常熟悉了,今天我們就以此為入口一步一步的帶你深入其底層世界。
// ReentrantLock --> lokc() 實(shí)現(xiàn)Lock 接口的方法 public void lock() { // 調(diào)用內(nèi)部類sync 的lock方法, 這里有兩種實(shí)現(xiàn),公平鎖(FairSync)非公平鎖(NonfairSync)這里我們來(lái)主要說(shuō) NonfairSync sync.lock(); }
ReentrantLock 的lock 方法, sync 為ReentrantLock的一個(gè)內(nèi)部類,其繼承了AbstractQueuedSynchronizer(AQS), 他有兩個(gè)子類公平鎖FairSync 和非公平鎖NonfairSync
ReentrantLock 中其中大部分的功能的實(shí)現(xiàn)都是委托給內(nèi)部類Sync實(shí)現(xiàn)的,在Sync 中定義了abstract void lock() 留給子類去實(shí)現(xiàn), 默認(rèn)實(shí)現(xiàn)了final boolean nonfairTryAcquire(int acquires) 方法,可以看出其為非公平鎖默認(rèn)實(shí)現(xiàn)方式,下面我講下給看下非公平鎖lock方法。
// ReentrantLock$NonfairSync final void lock() { if (compareAndSetState(0, 1)) // 非公平原則, 上來(lái)就插隊(duì)來(lái)嘗試下獲取共享狀態(tài),如果成功則設(shè)置當(dāng)前持有鎖線程為自己,獲取鎖成功。 setExclusiveOwnerThread(Thread.currentThread()); else //如果失敗則調(diào)用AQS中的acquire方法 acquire(1); }
首先就嘗試獲取同步狀態(tài)(體現(xiàn)非公平鎖上來(lái)就插隊(duì))如果成功則將持有鎖線程設(shè)置為自己,失敗則走AQS中的acquire方法。
// AQS中的acquire方法,在AQS中已經(jīng)講過(guò),首先會(huì)調(diào)用tryAcquire(arg)方法,tryAcquire(arg)方法會(huì)有具體由子類去實(shí)現(xiàn)。 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
這里AQS中的源碼我就不再過(guò)多的講解了(無(wú)非就是嘗試獲取同步狀態(tài)成功直接返回,失敗加入同步隊(duì)列等待被喚醒),主要來(lái)將留給子類實(shí)現(xiàn)的tryAcquire(arg)方法。
如有對(duì)AQS不明白的請(qǐng)看文章頭中列出的幾篇文章過(guò)一下或者鎖搜引擎中鎖搜下。
protected final boolean tryAcquire(int acquires) { // 非公平鎖的tryAcquire(arg)實(shí)現(xiàn),委托給Sync.nonfairTryAcquire(int acquires)具體處理 return nonfairTryAcquire(acquires); }
ReentrantLock中非公平鎖tryAcquire(int acquires)的實(shí)現(xiàn),具體調(diào)用其父類Sync中默認(rèn)實(shí)現(xiàn)的(上面已經(jīng)提過(guò))。
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); // 獲取共享狀態(tài) int c = getState(); if (c == 0) { // 如果共享狀態(tài)為0,說(shuō)明鎖空閑,利用CAS來(lái)獲取鎖(將共享狀態(tài)值改為1) if (compareAndSetState(0, acquires)) { // 如果設(shè)置成功,則表明獲取鎖成功,將持有鎖線程設(shè)置為自己 setExclusiveOwnerThread(current); return true; } } // 如果c != 0 則說(shuō)明鎖已經(jīng)被線程持有,判斷持有鎖的線程是不是自己(這里就是可重入鎖的具體體現(xiàn)) else if (current == getExclusiveOwnerThread()) { // 如果當(dāng)前持有鎖的線程是自己,說(shuō)明可重入,將共享狀態(tài)值加1,返回ture int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
主要邏輯:
首先判斷同步狀態(tài) state == 0 ?,
如果state == 0 則說(shuō)明該鎖處于空閑狀態(tài),直接通過(guò)CAS設(shè)置同步狀態(tài),成功將持有鎖線程設(shè)置為自己返回ture,
如果state !=0 判斷鎖的持有者是否是自己,是則說(shuō)明可重入將state 值加1 返回ture,
否則返回false.
來(lái)張圖加深下理解
注:此圖只是體現(xiàn)了RenntrantLock中的狀態(tài),其中涉及到AQS中的狀態(tài)流轉(zhuǎn)沒(méi)有在這體現(xiàn)。
鎖的釋放邏輯就比較簡(jiǎn)單
public void unlock() { sync.release(1); }
同樣在ReentrantLock.unlock()方法中將具體釋放邏輯委托給了內(nèi)部類Sync來(lái)實(shí)現(xiàn), 在這Sync 同樣沒(méi)有去實(shí)現(xiàn)release(1)而是使用其父類AQS的默認(rèn)實(shí)現(xiàn)。
// 調(diào)用AQS中的release 方法 public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
AQS釋放鎖的邏輯比較簡(jiǎn)單,同樣就不解釋了(無(wú)非就是釋放鎖,喚醒后繼節(jié)點(diǎn))具體來(lái)看下需要自類實(shí)現(xiàn)的tryRelease(arg) 釋放共享狀態(tài)的方法。
protected final boolean tryRelease(int releases) { // 共享狀態(tài)值減去releases int c = getState() - releases; // 如果持有鎖的線程線程不是自己,則拋出異常(很好理解不能把別人的鎖釋放了) if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; // 共享狀態(tài) state = 0 則表明釋放鎖成功 if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
釋放共享狀態(tài)(鎖)的邏輯比較簡(jiǎn)單,主要是將共享狀態(tài)的值減去releases,減后共享狀態(tài)值為0表示釋放鎖成功將持有鎖線程設(shè)置為null 返回 ture。
最后我們來(lái)對(duì)ReentrantLock加鎖和釋放鎖做個(gè)簡(jiǎn)單總結(jié),ReentrantLock 是一個(gè)可重入鎖提供了兩種實(shí)現(xiàn)方式公平鎖和非公平鎖。
非公平鎖獲取鎖流程:
1: 首先不管三七二一就來(lái)個(gè) CAS 嘗試獲取鎖。
2: 成功則皆大歡喜。
3: 失敗,再次獲取下共享狀態(tài)(萬(wàn)一這會(huì)有人釋放了尼)判斷是否為0
4: 如果為0 則說(shuō)明鎖空閑,再次CAS獲取鎖成功將持有鎖線程設(shè)置為自己并返回ture
5:不為0,判斷持有者是否是自己、是自己表明可重入state + 1 返回ture 否則返回false(就去同步隊(duì)列中排隊(duì)去)。
非公平鎖釋放鎖流程
很簡(jiǎn)單state - 1 = 0 則釋放成功否則失敗。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/74316.html
摘要:接著線程過(guò)來(lái)通過(guò)方式獲取鎖,獲取鎖的過(guò)程就是通過(guò)操作變量將其值從變?yōu)椤>€程加鎖成功后還有一步重要的操作,就是將設(shè)置成為自己。線程屁顛屁顛的就去等待區(qū)小憩一會(huì)去了。 一、寫(xiě)在前面 這篇文章,我們聊一聊Java并發(fā)中的核武器, AQS底層實(shí)現(xiàn)。 不管是工作三四年、還是五六年的在工作或者面試中涉及到并發(fā)的是時(shí)候總是繞不過(guò)AQS這個(gè)詞。 首先,確實(shí)還有很多人連AQS是什么都不知道,甚至有的竟...
摘要:公平鎖阻塞隊(duì)列前邊有線程,要去后邊排隊(duì),簡(jiǎn)單來(lái)說(shuō)滾后邊等著去。非公平鎖不管是否有線程排隊(duì),先槍鎖基于實(shí)現(xiàn)的可重入鎖實(shí)現(xiàn)類。 AQS原理介紹: AQS (Abstra...
摘要:所以就有了讀寫(xiě)鎖。只要沒(méi)有,讀取鎖可以由多個(gè)線程同時(shí)保持。其讀寫(xiě)鎖為兩個(gè)內(nèi)部類都實(shí)現(xiàn)了接口。讀寫(xiě)鎖同樣依賴自定義同步器來(lái)實(shí)現(xiàn)同步狀態(tài)的,而讀寫(xiě)狀態(tài)就是其自定義同步器的狀態(tài)。判斷申請(qǐng)寫(xiě)鎖數(shù)量是否超標(biāo)超標(biāo)則直接異常,反之則設(shè)置共享狀態(tài)。 一、寫(xiě)在前面 在上篇我們聊到了可重入鎖(排它鎖)ReentrantLcok ,具體參見(jiàn)《J.U.C|可重入鎖ReentrantLock》 Reentra...
摘要:所以就有了讀寫(xiě)鎖。只要沒(méi)有,讀取鎖可以由多個(gè)線程同時(shí)保持。其讀寫(xiě)鎖為兩個(gè)內(nèi)部類都實(shí)現(xiàn)了接口。讀寫(xiě)鎖同樣依賴自定義同步器來(lái)實(shí)現(xiàn)同步狀態(tài)的,而讀寫(xiě)狀態(tài)就是其自定義同步器的狀態(tài)。判斷申請(qǐng)寫(xiě)鎖數(shù)量是否超標(biāo)超標(biāo)則直接異常,反之則設(shè)置共享狀態(tài)。 一、寫(xiě)在前面 在上篇我們聊到了可重入鎖(排它鎖)ReentrantLcok ,具體參見(jiàn)《J.U.C|可重入鎖ReentrantLock》 Reentra...
摘要:的主要功能和關(guān)鍵字一致,均是用于多線程的同步。而僅支持通過(guò)查詢當(dāng)前線程是否持有鎖。由于和使用的是同一把可重入鎖,所以線程可以進(jìn)入方法,并再次獲得鎖,而不會(huì)被阻塞住。公平與非公平公平與非公平指的是線程獲取鎖的方式。 1.簡(jiǎn)介 可重入鎖ReentrantLock自 JDK 1.5 被引入,功能上與synchronized關(guān)鍵字類似。所謂的可重入是指,線程可對(duì)同一把鎖進(jìn)行重復(fù)加鎖,而不會(huì)被阻...
閱讀 3223·2021-11-23 09:51
閱讀 1030·2021-08-05 09:58
閱讀 663·2019-08-29 16:05
閱讀 971·2019-08-28 18:17
閱讀 3028·2019-08-26 14:06
閱讀 2721·2019-08-26 12:20
閱讀 2154·2019-08-26 12:18
閱讀 3064·2019-08-26 11:56