国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Java教程 - 從ReentrantLock的角度思考AQS

從ReentrantLock的角度思考AQS

2021-12-09 22:13Java賊船Captain Java教程

我們上一篇簡(jiǎn)單介紹了AQS這個(gè)技術(shù)點(diǎn),這一篇我們從ReentrantLock這個(gè)鎖的角度來分析AQS,幫助大家理解。

從ReentrantLock的角度思考AQS

我們上一篇簡(jiǎn)單介紹了AQS這個(gè)技術(shù)點(diǎn),這一篇我們從ReentrantLock這個(gè)的角度來分析AQS,幫助大家理解

從ReentrantLock的角度思考AQS

首先,我們先看一下ReentrantLock的內(nèi)部的抽象類Sync,這個(gè)是繼承于AQS的,重寫了其中的一些方法,我們會(huì)在下面源碼中解析,繼續(xù)往下看,記住這個(gè)Sync

從ReentrantLock的角度思考AQS

我們知道這個(gè)鎖可以實(shí)現(xiàn)公平鎖和非公平鎖,我們來看下是如何實(shí)現(xiàn)的

  1. /**
  2. * Sync object for non-fair locks
  3. */
  4. static final class NonfairSync extends Sync {
  5. private static final long serialVersionUID = 7316153563782823691L;
  6. /**
  7. * Performs lock. Try immediate barge, backing up to normal
  8. * acquire on failure.
  9. */
  10. final void lock() {
  11. if (compareAndSetState(0, 1))
  12. setExclusiveOwnerThread(Thread.currentThread());
  13. else
  14. acquire(1);
  15. }
  16. protected final boolean tryAcquire(int acquires) {
  17. return nonfairTryAcquire(acquires);
  18. }
  19. }
  20. /**
  21. * Sync object for fair locks
  22. */
  23. static final class FairSync extends Sync {
  24. private static final long serialVersionUID = -3000897897090466540L;
  25. final void lock() {
  26. acquire(1);
  27. }}

上面的是非公平鎖,下面的是公平鎖,默認(rèn)的是非公平鎖,我們看下非公平鎖的實(shí)現(xiàn)是先通過CAS的方式去加鎖,加鎖成功之后就將當(dāng)前線程設(shè)置為活躍的持有鎖的線程

  1. /**
  2. * The current owner of exclusive mode synchronization.
  3. */
  4. private transient Thread exclusiveOwnerThread;

失敗的話會(huì)執(zhí)行acquire方法,OK,這里我們?cè)倏聪鹿芥iFairSync的lock方法的實(shí)現(xiàn),這個(gè)公平鎖沒有像上面非公平鎖那樣判斷,而是直接調(diào)用了acquire方法

這里大家應(yīng)該也懂了非公平鎖和公平鎖的真正區(qū)別了吧,就是非公平鎖的時(shí)候,線程來的時(shí)候會(huì)多一次直接嘗試加鎖,剩下的操作就是一樣了

OK,讓我們進(jìn)去acquire方法看

從ReentrantLock的角度思考AQS

看一下tryAcquire方法

從ReentrantLock的角度思考AQS

可以看出,這里只是AQS的簡(jiǎn)單實(shí)現(xiàn),具體獲取鎖的實(shí)現(xiàn)方法是由各自的公平鎖和非公平鎖單獨(dú)實(shí)現(xiàn)的(以ReentrantLock為例)

如果該方法返回了True,則說明當(dāng)前線程獲取鎖成功,就不用往后執(zhí)行了;如果獲取失敗,就需要加入到等待隊(duì)列中。下面會(huì)詳細(xì)解釋線程是何時(shí)以及怎樣被加入進(jìn)等待隊(duì)列中的。

OK,知道了這個(gè)我們就得看看ReentrantLock是如何實(shí)現(xiàn)tryAcquire方法的

老規(guī)矩,先看一下非公平鎖中的具體實(shí)現(xiàn)

從ReentrantLock的角度思考AQS

從ReentrantLock的角度思考AQS

大家看代碼應(yīng)該也比較好理解,第一步先判斷state==0,這個(gè)0也就意味著這個(gè)共享資源處于空閑狀態(tài),于是這里就會(huì)先嘗試去搶一下鎖,假如此時(shí)等待隊(duì)列中有等待線程,則就是等待線程中的第二個(gè)節(jié)點(diǎn)和這個(gè)新加入的這個(gè)線程去搶這個(gè)鎖了

為什么是第二個(gè),因?yàn)榈谝粋€(gè)head節(jié)點(diǎn)存儲(chǔ)的永遠(yuǎn)是占用鎖的線程節(jié)點(diǎn)Node

接下來就是判斷當(dāng)前持有鎖的線程和當(dāng)前線程是否是同一個(gè),如果是同一個(gè),則將state+1,這里就是ReentrantLock支持重入性的關(guān)鍵,到時(shí)候解鎖的時(shí)候也是通過減去這個(gè)state計(jì)數(shù)的

搶到鎖或者重入鎖,都會(huì)返回true,返回true,加鎖方法就直接加鎖了

從ReentrantLock的角度思考AQS

如果既沒搶到鎖,又發(fā)現(xiàn)占用鎖的線程不是當(dāng)前線程,則返回false,繼續(xù)執(zhí)行

上面這是非公平鎖的tryAcquire方法,接下來咱再看這個(gè)公平鎖的tryAcquire方法

從ReentrantLock的角度思考AQS

這個(gè)也是先判斷狀態(tài)是否為0,這個(gè)的==0之后的處理邏輯就很明了了,直接通過hasQueuedPredecessors方法判斷隊(duì)列中是否有等待的節(jié)點(diǎn),如果沒有等待的節(jié)點(diǎn),則直接通過CAS的方式進(jìn)行判斷,然后就是把當(dāng)前線程設(shè)置為活躍線程

如果有等待的節(jié)點(diǎn),就會(huì)跳過CAS的判斷,緊接著會(huì)去判斷當(dāng)前線程和持有鎖的線程是否是同一個(gè)線程,如果是同一個(gè)線程,還是進(jìn)行計(jì)數(shù)+1,滿足可重入性

不是就返回false,此時(shí)tryAcquire方法返回false

此時(shí),我們?cè)侔岩暯抢氐絘cquire方法

從ReentrantLock的角度思考AQS

返回false之后,則會(huì)執(zhí)行addWaiter方法和acquireQueued方法

從ReentrantLock的角度思考AQS

這段代碼首先會(huì)創(chuàng)建一個(gè)和當(dāng)前線程綁定的Node節(jié)點(diǎn),Node為雙向鏈表。此時(shí)等待對(duì)內(nèi)中的tail指針為空,直接調(diào)用enq(node)方法將當(dāng)前線程加入等待隊(duì)列尾部:

從ReentrantLock的角度思考AQS

第一遍循環(huán)時(shí)tail指針為空,進(jìn)入if邏輯,使用CAS操作設(shè)置head指針,將head指向一個(gè)新創(chuàng)建的Node節(jié)點(diǎn)。

此時(shí)AQS中數(shù)據(jù):

從ReentrantLock的角度思考AQS

執(zhí)行完成之后,head、tail、t都指向第一個(gè)Node元素。

接著執(zhí)行第二遍循環(huán),進(jìn)入else邏輯,此時(shí)已經(jīng)有了head節(jié)點(diǎn),這里要操作的就是將線程二對(duì)應(yīng)的Node節(jié)點(diǎn)掛到head節(jié)點(diǎn)后面。此時(shí)隊(duì)列中就有了兩個(gè)Node節(jié)點(diǎn):

從ReentrantLock的角度思考AQS

addWaiter()方法執(zhí)行完后,會(huì)返回當(dāng)前線程創(chuàng)建的節(jié)點(diǎn)信息。繼續(xù)往后執(zhí)行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)邏輯,此時(shí)傳入的參數(shù)為線程二對(duì)應(yīng)的Node節(jié)點(diǎn)信息

從ReentrantLock的角度思考AQS

acquireQueued()這個(gè)方法會(huì)先判斷當(dāng)前傳入的Node對(duì)應(yīng)的前置節(jié)點(diǎn)是否為head,如果是則嘗試加鎖。

加鎖成功過則將當(dāng)前節(jié)點(diǎn)設(shè)置為head節(jié)點(diǎn),然后空置之前的head節(jié)點(diǎn),方便后續(xù)被垃圾回收掉。

如果加鎖失敗或者Node的前置節(jié)點(diǎn)不是head節(jié)點(diǎn),就會(huì)通過shouldParkAfterFailedAcquire方法 將head節(jié)點(diǎn)的waitStatus變?yōu)榱薙IGNAL=-1,最后執(zhí)行parkAndChecknIterrupt方法,調(diào)用LockSupport.park()掛起當(dāng)前線程。

我們不能發(fā)現(xiàn)的一點(diǎn),就是AQS的設(shè)計(jì)內(nèi)部,包括ReentrantLock的設(shè)計(jì)內(nèi)部。很多地方都會(huì)嘗試用CAS的方式去加鎖,就是因?yàn)樵诟咚俚倪\(yùn)轉(zhuǎn)下,可能在幾行代碼的時(shí)間一個(gè)線程就已經(jīng)用完鎖了,這樣可以最高效率的來利用資源

parkAndCheckInterrupt主要用于掛起當(dāng)前線程,阻塞調(diào)用棧,返回當(dāng)前線程的中斷狀態(tài)。

給大家看個(gè)這里的流程圖,圖片來源于網(wǎng)絡(luò),覺得挺不錯(cuò)

從ReentrantLock的角度思考AQS

從上圖可以看出,跳出當(dāng)前循環(huán)的條件是當(dāng)“前置節(jié)點(diǎn)是頭結(jié)點(diǎn),且當(dāng)前線程獲取鎖成功”。

為了防止因死循環(huán)導(dǎo)致CPU資源被浪費(fèi),我們會(huì)判斷前置節(jié)點(diǎn)的狀態(tài)來決定是否要將當(dāng)前線程掛起,具體掛起流程用流程圖表示如下(shouldParkAfterFailedAcquire流程):

從ReentrantLock的角度思考AQS

acquireQueued中最后的finally中,如果失敗,則執(zhí)行cancelAcquire

獲取當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn),如果前驅(qū)節(jié)點(diǎn)的狀態(tài)是CANCELLED,那就一直往前遍歷,找到第一個(gè)waitStatus <= 0的節(jié)點(diǎn),將找到的Pred節(jié)點(diǎn)和當(dāng)前Node關(guān)聯(lián),將當(dāng)前Node設(shè)置為CANCELLED。

但是為什么所有的變化都是對(duì)Next指針進(jìn)行了操作,而沒有對(duì)Prev指針進(jìn)行操作呢?什么情況下會(huì)對(duì)Prev指針進(jìn)行操作?

執(zhí)行cancelAcquire的時(shí)候,當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)可能已經(jīng)從隊(duì)列中出去了(已經(jīng)執(zhí)行過Try代碼塊中的shouldParkAfterFailedAcquire方法了),如果此時(shí)修改Prev指針,有可能會(huì)導(dǎo)致Prev指向另一個(gè)已經(jīng)移除隊(duì)列的Node,因此這塊變化Prev指針不安全。

shouldParkAfterFailedAcquire方法中,會(huì)執(zhí)行下面的代碼,其實(shí)就是在處理Prev指針。shouldParkAfterFailedAcquire是獲取鎖失敗的情況下才會(huì)執(zhí)行,進(jìn)入該方法后,說明共享資源已被獲取,當(dāng)前節(jié)點(diǎn)之前的節(jié)點(diǎn)都不會(huì)出現(xiàn)變化,因此這個(gè)時(shí)候變更Prev指針比較安全。

  1. do {
  2. node.prev = pred = pred.prev;
  3. } while (pred.waitStatus > 0);

解鎖

接下來再對(duì)解鎖的基本流程進(jìn)行分析。由于ReentrantLock在解鎖的時(shí)候,并不區(qū)分公平鎖和非公平鎖,所以我們直接看解鎖的源碼:

從ReentrantLock的角度思考AQS

點(diǎn)進(jìn)來release之后發(fā)現(xiàn)實(shí)現(xiàn)還是在AQS框架中

從ReentrantLock的角度思考AQS

在ReentrantLock里面的公平鎖和非公平鎖的父類Sync定義了可重入鎖的釋放鎖機(jī)制。

從ReentrantLock的角度思考AQS

這個(gè)方法先去減少一次可重入次數(shù),然后判斷當(dāng)前線程是否是持有鎖的線程,如果不是,則直接拋出異常

接著判斷c==0,等于0代表當(dāng)前的資源處于空閑狀態(tài),便可以將當(dāng)前獨(dú)占資源的線程設(shè)置為null,然后更新state

如果不等于0,這一步釋放獨(dú)占鎖的操作便會(huì)濾過,就是普通的重入鎖減少一次重入次數(shù),就像是重入加鎖三次,執(zhí)行這里之后只是變成2次而已,但是還是該線程持有該資源

總結(jié)

我們先是在非公平鎖和公平鎖的角度分別分析了加鎖的過程,得知非公平比公平鎖只是多了一個(gè)搶先加鎖的機(jī)會(huì),但是如果搶不到鎖還是會(huì)執(zhí)行和公平鎖相同的邏輯

中間我們分析了公平鎖和非公平鎖的優(yōu)缺點(diǎn),這個(gè)是面試熱點(diǎn)

然后我們還會(huì)發(fā)現(xiàn)代碼中很多地方都會(huì)嘗試用CAS的方式去搶占鎖,我們知道CPU的運(yùn)行是很快的,這樣能夠保證資源釋放釋放能夠在第一時(shí)間被等待隊(duì)列中的線程搶到鎖

最后我們又分析了這個(gè)釋放鎖的過程,這個(gè)釋放鎖并沒有公平和非公平的區(qū)分,只是其中對(duì)于重入鎖進(jìn)行了處理,就是上面最后一張圖的==0操作,因?yàn)槲覀兩厦娣治隽酥厝氲牡览硪彩菍?duì)這個(gè)state進(jìn)行累加得來的,所以這里只需要減一,然后判斷是否為0即可

0的時(shí)候就意味著此時(shí)資源處于空閑狀態(tài),這個(gè)state是volatile的,保證了可見性

這篇只是一個(gè)籠統(tǒng)的分析,其實(shí)還有很多細(xì)節(jié)沒有分析到位,只能說AQS的設(shè)計(jì)很精妙,李老牛皮

原文鏈接:https://mp.weixin.qq.com/s/O3zw_Pjdrzdkh4xlNlJlew

延伸 · 閱讀

精彩推薦
  • Java教程升級(jí)IDEA后Lombok不能使用的解決方法

    升級(jí)IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級(jí),尋思已經(jīng)有好久沒有升過級(jí)了。升級(jí)完畢重啟之后,突然發(fā)現(xiàn)好多錯(cuò)誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java實(shí)現(xiàn)搶紅包功能

    Java實(shí)現(xiàn)搶紅包功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)搶紅包功能,采用多線程模擬多人同時(shí)搶紅包,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程20個(gè)非常實(shí)用的Java程序代碼片段

    20個(gè)非常實(shí)用的Java程序代碼片段

    這篇文章主要為大家分享了20個(gè)非常實(shí)用的Java程序片段,對(duì)java開發(fā)項(xiàng)目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java8中Stream使用的一個(gè)注意事項(xiàng)

    Java8中Stream使用的一個(gè)注意事項(xiàng)

    最近在工作中發(fā)現(xiàn)了對(duì)于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個(gè)非常重要的注意點(diǎn),所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
  • Java教程Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程xml與Java對(duì)象的轉(zhuǎn)換詳解

    xml與Java對(duì)象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對(duì)象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 国产模特私拍xxxx | 欧美激情视频一区二区三区 | 激情久久久 | 午夜在线视频 | 视频在线一区 | 黄色mm视频 | 亚洲在线视频 | 中文字幕亚洲视频 | 中文永久免费观看 | 国产精品一二区 | 国产精品区二区三区日本 | h色视频在线观看 | 91精品国产高清一区二区三区 | 亚洲久久 | 久久99视频精品 | 国产精品视频一区二区三区不卡 | 成人在线免费观看视频 | 精品自拍视频在线观看 | 欧美成人精精品一区二区频 | 成人爽a毛片一区二区免费 日韩av高清在线 | 中文字幕1区 | 国产精品福利在线观看 | 6080亚洲精品一区二区 | 欧美成年黄网站色视频 | 天天拍拍天天干 | 性农村人freesex | 精品国产一区探花在线观看 | 国产福利二区 | 欧美中文字幕 | 中文字幕日韩av | 免费一级视频在线观看 | 日本三级电影网站 | 黄视频免费 | 精品一区二区av | 婷婷激情五月 | 中文字幕在线日韩 | 国产精品18久久久久久久久 | 精品成人国产在线观看男人呻吟 | 天天艹视频 | 黄片毛片在线观看 | 深夜精品 |