最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Java并發(fā)編程-淺談ReentranLock

2021-04-21 09:50 作者:光耀三十洲  | 我要投稿

1. 產(chǎn)生背景

在Java中已經(jīng)有內(nèi)置鎖synchronized的情況下,為什么還需要引入ReentranLock呢?

synchronized是基于 JVM內(nèi)置鎖實現(xiàn),通過內(nèi)部對象Monitor(監(jiān)視器鎖)實現(xiàn),基于進(jìn)入與退出Monitor對象實現(xiàn)方法與代碼塊同步,監(jiān)視器鎖的實現(xiàn)依賴底層操作系統(tǒng)的Mutex lock(互斥鎖)實現(xiàn),被阻塞的線程會被掛起、等待重新調(diào)度,會導(dǎo)致“用戶態(tài)和內(nèi)核態(tài)”兩個態(tài)之間來回切換,對性能有較大影響。

學(xué)習(xí)更多,請點擊:https://www.bilibili.com/video/BV1Qb4y1D75J

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1qo4y1f7Uw

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1s64y1i77s

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1g84y1F7vS

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1a54y1b7hh

基于這樣一個背景,Doug Lea在JDK1.5中提供了API層面的互斥鎖ReentranLock,實現(xiàn)了可重入、可中斷、公平鎖等特性。在synchronized優(yōu)化之前,synchronized的性能比起ReentranLock還是有差距的。

2. 簡單使用

3. 源碼分析

3.1 ReentrantLock結(jié)構(gòu)

首先,RentrantLock對象結(jié)構(gòu),有個大致的了解。

學(xué)習(xí)更多,請點擊:https://www.bilibili.com/video/BV1Qb4y1D75J

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1qo4y1f7Uw

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1s64y1i77s

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1g84y1F7vS

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1a54y1b7hh

3.2 加鎖 lock.lock()

其實,ReentrantLock中的方法都是由成員對象sync完成的。

所以,我們直接進(jìn)入 NonfairSync.lock()

學(xué)習(xí)更多,請點擊:https://www.bilibili.com/video/BV1Qb4y1D75J

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1qo4y1f7Uw

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1s64y1i77s

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1g84y1F7vS

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1a54y1b7hh

接下來,分析 acquire()中的三個重點方法:

  1. tryAcquire():嘗試獲取鎖,由子類重寫(這里實現(xiàn)了可重入、公平鎖特性);

  2. addWaiter():AQS中維護(hù)了一個鏈表,將當(dāng)前線程包裝成一個Node節(jié)點,入隊;

  3. acquireQueued():以獨占不可中斷模式獲取已經(jīng)在隊列中的線程。

3.2.1 tryAcquire

當(dāng)前方法無論是獲取到了鎖,還是重復(fù)加鎖,都是返回true。上面的 acquire()就會直接結(jié)束,不會繼續(xù)執(zhí)行后續(xù)的入隊操作。

學(xué)習(xí)更多,請點擊:https://www.bilibili.com/video/BV1Qb4y1D75J

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1qo4y1f7Uw

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1s64y1i77s

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1g84y1F7vS

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1a54y1b7hh

非公平鎖的特性就是在這個方法中體現(xiàn)出來的(其實lock方法中也是直接獲取鎖,更直接)。

3.2.2 addWaiter

注意,當(dāng)前方法調(diào)用時,傳入了一個參數(shù):Node.EXCLUSIVE(獨占模式)。這個參數(shù)實際上是起到了一個標(biāo)識的作用,有兩種類型:獨占、共享。

addWaiter()的職責(zé)很簡單,就是將當(dāng)前線程包裝成一個Node節(jié)點,然后設(shè)置為隊尾(必要時初始化隊列),通過死循環(huán)的形式保證一定入隊成功。

學(xué)習(xí)更多,請點擊:https://www.bilibili.com/video/BV1Qb4y1D75J

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1qo4y1f7Uw

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1s64y1i77s

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1g84y1F7vS

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1a54y1b7hh

下面,大致了解一下Node的結(jié)構(gòu)(AbstractQueuedSynchronizer的內(nèi)部類)

3.2.3 acquireQueued

代碼執(zhí)行到這里,說明節(jié)點(當(dāng)前線程)已經(jīng)成功入隊。如果當(dāng)前節(jié)點就是第一位候選者,就會嘗試去獲取鎖。但是鎖有可能還沒有釋放掉(state != 0),獲取鎖失敗,就會阻塞當(dāng)前線程。

學(xué)習(xí)更多,請點擊:https://www.bilibili.com/video/BV1Qb4y1D75J

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1qo4y1f7Uw

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1s64y1i77s

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1g84y1F7vS

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1a54y1b7hh


相信大家也看到了,這個方法中有段代碼的寫法太(bi)過(jiao)精(e)簡(xin)。

image

其中,紅色方塊的pred和N1狀態(tài)都是已取消的(狀態(tài)值為1)。

首先,執(zhí)行if判斷時,發(fā)現(xiàn)pred的等候狀態(tài)是已取消的,執(zhí)行do代碼塊,將pred指向N1節(jié)點。執(zhí)行while判斷,發(fā)現(xiàn)N1節(jié)點的狀態(tài)也是已取消的,再次執(zhí)行do代碼塊,將pred指向N2節(jié)點。執(zhí)行while判斷,N2節(jié)點狀態(tài)并不是已取消,循環(huán)結(jié)束。而在循環(huán)結(jié)束之前node的前驅(qū)指針已經(jīng)指向了N2,循環(huán)結(jié)束后再將N2的后驅(qū)指針指向node,兩者就建立了雙向關(guān)聯(lián)。

學(xué)習(xí)更多,請點擊:https://www.bilibili.com/video/BV1Qb4y1D75J

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1qo4y1f7Uw

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1s64y1i77s

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1g84y1F7vS

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1a54y1b7hh

細(xì)心的你一定發(fā)現(xiàn)了,在acquireQueued()方法中的死循環(huán)上注釋了一般只會執(zhí)行三次,那么為什么說會執(zhí)行三次呢?

假設(shè)現(xiàn)在lock鎖已經(jīng)被某個線程獲取了,并且還在執(zhí)行同步代碼塊,沒有來得及釋放鎖。這時,線程A也來執(zhí)行了業(yè)務(wù)方法,然后嘗試獲取鎖,必然獲取失敗進(jìn)而執(zhí)行入隊操作。此時,由當(dāng)前線程包裝的Node節(jié)點占據(jù)隊尾,隊頭是初始化的一個“空”節(jié)點。
參數(shù)pred 實際上就是head節(jié)點(狀態(tài)值為0)。第一次循環(huán),執(zhí)行else邏輯,將前驅(qū)節(jié)點pred的狀態(tài)值設(shè)置為SIGNAL,注意返回的是false,這將導(dǎo)致當(dāng)前方法所在if判斷直接結(jié)束;第二次循環(huán),首個if判斷生效,返回true,執(zhí)行parkAndCheckInterrupt(),阻塞線程;直到線程被喚醒后,開啟第三次循環(huán),獲取鎖成功,直接返回,結(jié)束死循環(huán)。

注意:以上描述,隊列中不存在已取消的節(jié)點并且鎖不會被爭搶,故而說是一般情況下。

不得不說,AQS的方法名取的還是很貼切的,基本上見名知意了。阻塞當(dāng)前線程并返回線程中斷狀態(tài)!

我們先來了解一下 LockSupport.park(this)的功能點:

  • 阻塞當(dāng)前線程的執(zhí)行,且不會釋放當(dāng)前線程占有的鎖資源;

  • 可以被另一個線程調(diào)用 LockSupport.unpark()方法喚醒;

  • 底層調(diào)用 Unsafe的native方法。

3.2.4 總結(jié)

ReentranLock的一套加鎖流程總結(jié)下來,就是嘗試獲取鎖,獲取成功,更新鎖狀態(tài)、設(shè)置獨占線程;獲取失敗,將當(dāng)前線程包裝成一個Node節(jié)點,加入到AQS內(nèi)部維護(hù)的一個鏈表的尾部,最后阻塞當(dāng)前線程,直到被喚醒,再次嘗試獲取鎖(非公平鎖,存在被爭搶的可能性)。

學(xué)習(xí)更多,請點擊:https://www.bilibili.com/video/BV1Qb4y1D75J

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1qo4y1f7Uw

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1s64y1i77s

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1g84y1F7vS

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1a54y1b7hh

3.3 解鎖 lock.unlock()

加鎖時自我阻塞的線程是如何被喚醒的,觸發(fā)的機制又是怎樣的?

接下來,讓我們一步步的分析ReentranLock的另一個重要組件。

需要注意的是加鎖和解鎖的次數(shù)要一致,不然將會導(dǎo)致隊列中的等候線程無法被喚醒。

3.3.2 unparkSuccessor

預(yù)喚醒的節(jié)點s 是head的后驅(qū)節(jié)點(FIFO)。

但是如果s 為空或者已取消時,就需要從隊列中找出一個正常的節(jié)點。怎么找呢? 以隊尾節(jié)點為起始點,向前遍歷,最終s指向的是隊列中最靠近隊頭的某個正常節(jié)點。

隨后,通過調(diào)用 LockSupport.unpark()的方式,喚醒 s節(jié)點持有的線程。

至此,ReentranLock一套完整的加鎖解鎖流程就分析完畢了~

學(xué)習(xí)更多,請點擊:https://www.bilibili.com/video/BV1Qb4y1D75J

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1qo4y1f7Uw

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1s64y1i77s

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1g84y1F7vS

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.bilibili.com/video/BV1a54y1b7hh


作者:Hey Max!
鏈接:https://juejin.cn/post/6953154315603640351
來源:掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。


Java并發(fā)編程-淺談ReentranLock的評論 (共 條)

分享到微博請遵守國家法律
岫岩| 通海县| 蕉岭县| 华宁县| 峡江县| 贺州市| 安龙县| 宕昌县| 高雄市| 台东市| 连南| 玉门市| 喀什市| 沙湾县| 墨玉县| 玉树县| 孟村| 平山县| 吉木乃县| 贡觉县| 大埔县| 思茅市| 新乡市| 徐州市| 五寨县| 乌拉特中旗| 环江| 南川市| 奉化市| 永年县| 黔西县| 微山县| 西青区| 合川市| 当涂县| 维西| 揭西县| 靖安县| 隆林| 安宁市| 苏尼特左旗|