成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

ReentrantLock核心原理,絕對干貨

開發 前端
面試官:我看你熟悉ReentrantLock源碼,能講講他的中斷鎖是怎么實現的么?不知道也沒關系,看完這篇文章通過你的思考,能找到答案哦!

 [[350997]]

前言

先來一個面試題:

面試官:我看你熟悉ReentrantLock源碼,能講講他的中斷鎖是怎么實現的么?

不知道也沒關系,看完這篇文章通過你的思考,能找到答案哦

那我們開始吧

ReentrantLock 中文我們叫做可重入互斥鎖,可重入的意思是同一個線程可以對同一個共享資源重復的加鎖或釋放鎖,互斥就是 AQS 中的排它鎖的意思,只允許一個線程獲得鎖。

簡單應用

ReentrantLock 的使用相比較 synchronized 會稍微繁瑣一點,所謂顯示鎖,也就是你在代碼中需要主動的去進行 lock 操作。一般來講我們可以按照下面的方式使用 ReentrantLock

  1. Lock lock = new ReentrantLock(); 
  2. lock.lock(); 
  3. try { 
  4.   doSomething(); 
  5. }finally { 
  6.   lock.unlock(); 

lock.lock () 就是在顯式的上鎖。上鎖后,下面的代碼塊一定要放到 try 中,并且要結合 finally 代碼塊調用lock.unlock ()來釋放鎖,否則一定 doSomething 方法中出現任何異常,這個鎖將永遠不會被釋放掉。

公平鎖和非公平鎖

synchronized 是非公平鎖,也就是說每當鎖匙放的時候,所有等待鎖的線程并不會按照排隊順去依次獲得鎖,而是會再次去爭搶鎖。ReentrantLock 相比較而言更為靈活,它能夠支持公平和非公平鎖兩種形式。只需要在聲明的時候傳入 true。

  1. Lock lock = new ReentrantLock(true); 

而默認的無參構造方法則會創建非公平鎖。

tryLock方法

前面我們通過 lock.lock (); 來完成加鎖,此時加鎖操作是阻塞的,直到獲取鎖才會繼續向下進行。ReentrantLock 其實還有更為靈活的枷鎖方式 tryLock。

tryLock 方法有兩個重載,第一個是無參數的 tryLock 方法,被調用后,該方法會立即返回獲取鎖的情況。獲取為 true,未能獲取為 false。我們的代碼中可以通過返回的結果進行進一步的處理。第二個是有參數的 tryLock 方法,通過傳入時間和單位,來控制等待獲取鎖的時長。如果超過時間未能獲取鎖則放回 false,反之返回 true。使用方法如下:

  1. if(lock.tryLock(2, TimeUnit.SECONDS)){ 
  2.    try { 
  3.       doSomething(); 
  4.    } catch (InterruptedException e) { 
  5.       e.printStackTrace(); 
  6.    }finally { 
  7.       lock.unlock(); 
  8.    } 
  9. }else
  10.   doSomethingElse(); 

我們如果不希望無法獲取鎖時一直等待,而是希望能夠去做一些其它事情時,可以選擇此方式。

類結構

ReentrantLock 類本身是不繼承 AQS 的,實現了 Lock 接口,如下:

  1. public class ReentrantLock implements Lock, java.io.Serializable {} 

Lock 接口定義了各種加鎖,釋放鎖的方法,接口有如下幾個:

  1. // 獲得鎖方法,獲取不到鎖的線程會到同步隊列中阻塞排隊 
  2. void lock(); 
  3. // 獲取可中斷的鎖 
  4. void lockInterruptibly() throws InterruptedException; 
  5. // 嘗試獲得鎖,如果鎖空閑,立馬返回 true,否則返回 false 
  6. boolean tryLock(); 
  7. // 帶有超時等待時間的鎖,如果超時時間到了,仍然沒有獲得鎖,返回 false 
  8. boolean tryLock(long time, TimeUnit unit) throws InterruptedException; 
  9. // 釋放鎖 
  10. void unlock(); 
  11. // 得到新的 Condition 
  12. Condition newCondition(); 

ReentrantLock 就負責實現這些接口,我們使用時,直接面對的也是這些方法,這些方法的底層實現都是交給 Sync 內部類去實現的,Sync 類的定義如下:

  1. abstract static class Sync extends AbstractQueuedSynchronizer {} 

都是最終繼承自 AbstractQueuedSynchronizer。這就是著名的 AQS。通過查看 AQS 的注釋我們了解到, AQS 依賴先進先出隊列實現了阻塞鎖和相關的同步器(信號量、事件等)。

AQS 內部有一個 volatile 類型的 state 屬性,實際上多線程對鎖的競爭體現在對 state 值寫入的競爭。一旦 state 從 0 變為 1,代表有線程已經競爭到鎖,那么其它線程則進入等待隊列。

等待隊列是一個鏈表結構的 FIFO 隊列,這能夠確保公平鎖的實現。同一線程多次獲取鎖時,如果之前該線程已經持有鎖,那么對 state 再次加 1。釋放鎖時,則會對 state-1。直到減為 0,才意味著此線程真正釋放了鎖。

Sync 繼承了 AbstractQueuedSynchronizer ,所以 Sync 就具有了鎖的框架,根據 AQS 的框架,Sync 只需要實現 AQS 預留的幾個方法即可,但 Sync 也只是實現了部分方法,還有一些交給子類 NonfairSync 和 FairSync 去實現了,NonfairSync 是非公平鎖,FairSync 是公平鎖,定義如下:

  1. // 同步器 Sync 的兩個子類鎖 
  2. static final class FairSync extends Sync {} 
  3. static final class NonfairSync extends Sync {} 

幾個類整體的結構如下:

圖中 Sync、NonfairSync、FairSync 都是靜態內部類的方式實現的,這個也符合 AQS 框架定義的實現標準。

構造器

ReentrantLock 構造器有兩種,代碼如下:

  1. // 無參數構造器,相當于 ReentrantLock(false),默認是非公平的 
  2. public ReentrantLock() { 
  3.     sync = new NonfairSync(); 
  4.   
  5. public ReentrantLock(boolean fair) { 
  6.     sync = fair ? new FairSync() : new NonfairSync(); 

無參構造器默認構造是非公平的鎖,有參構造器可以選擇。

從構造器中可以看出,公平鎖是依靠 FairSync 實現的,非公平鎖是依靠 NonfairSync 實現的

源碼解析

Sync同步器

Sync 表示同步器,繼承了 AQS:

從圖中可以看出,lock 方法是個抽象方法,留給 FairSync 和 NonfairSync 兩個子類去實現。

加鎖方法

FairSync公平鎖

FairSync 公平鎖只實現了 lock 和 tryAcquire 兩個方法,lock 方法非常簡單,如下:

  1. // acquire 是 AQS 的方法,表示先嘗試獲得鎖,失敗之后進入同步隊列阻塞等待 
  2. final void lock() { 
  3.     acquire(1); 

在 FairSync 并沒有重寫 acquire 方法代碼。調用的為 AbstractQueuedSynchronizer 的代碼,如下:

  1. public final void acquire(int arg) { 
  2.     if (!tryAcquire(arg) && 
  3.         acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
  4.         selfInterrupt(); 

首先調用一次 tryAcquire 方法。如果 tryAcquire 方法返回 true,那么 acquire 就會立即返回。

但如果 tryAcquire 返回了 false,那么則會先調用 addWaiter,把當前線程包裝成一個等待的 node,加入到等待隊列。然后調用 acquireQueued 嘗試排隊獲取鎖,如果成功后發現自己被中斷過,那么返回 true,導致 selfInterrupt 被觸發,這個方里只是調用Thread.currentThread ().interrupt ();進行 interrupt。

acquireQueued 代碼如下:

在此方法中進入自旋,不斷查看自己排隊的情況。如果輪到自己( header 是已經獲取鎖的線程,而 header 后面的線程是排隊到要去獲取鎖的線程),那么調用 tryAcquire 方法去獲取鎖,然后把自己設置為隊列的 header。在自旋中,如果沒有排隊到自己,還會檢查是否應該應該被中斷。

整個獲取鎖的過程我們可以總結下:

直接通過 tryAcquire 嘗試獲取鎖,成功直接返回;

如果沒能獲取成功,那么把自己加入等待隊列;

自旋查看自己的排隊情況;

如果排隊輪到自己,那么嘗試通過 tryAcquire 獲取鎖;

如果沒輪到自己,那么回到第三步查看自己的排隊情況。

從以上過程我們可以看到鎖的獲取是通過 tryAcquire 方法。而這個方法在 FairSync 和 NonfairSync 有不同實現。

這個tryAcquire 方法是 AQS 在 acquire 方法中留給子類實現的抽象方法,FairSync 中實現的源碼如下:

實際上它的實現和 NonfairSync 的實現,只是在 c==0 時,多了對 hasQueuedPredecessors 方法的調用。故名思義,這個方法做的事情就是判斷當前線程是否前面還有排隊的線程。

當它前面沒有排隊線程,說明已經排隊到自己了,這是才會通過 CAS 的的方式去改變 state 值為 1,如果成功,那么說明當前線程獲取鎖成功。接下來就是調用 setExclusiveOwnerThread 把自己設置成為鎖的擁有者。else if 中邏輯則是在處理重入邏輯,如果當前線程就是鎖的擁有者,那么會把 state 加 1 更新回去。

通過以上分析,我們可以看出 AbstractQueuedSynchronizer 提供 acquire 方法的模板邏輯,但其中真正對鎖的獲取方法 tryAcquire,是在不同子類中實現的,這是很好的設計思想。

NonfairSync非公平鎖

NonfairSync 底層實現了 lock 和 tryAcquire 兩個方法,如下:

nonfairTryAcquire

以上代碼有三點需要注意:

通過判斷 AQS 的 state 的狀態來決定是否可以獲得鎖,0 表示鎖是空閑的;

else if 的代碼體現了可重入加鎖,同一個線程對共享資源重入加鎖,底層實現就是把 state + 1,并且可重入的次數是有限制的,為 Integer 的最大值;

這個方法是非公平的,所以只有非公平鎖才會用到,公平鎖是另外的實現。

無參的 tryLock 方法調用的就是此方法,tryLock 的方法源碼如下:

  1. public boolean tryLock() { 
  2.     // 入參數是 1 表示嘗試獲得一次鎖 
  3.     return sync.nonfairTryAcquire(1); 

其底層的調用關系(只是簡單表明調用關系,并不是完整分支圖)如下:

解鎖方法

  1. public void unlock() { 
  2.     sync.release(1); 

和 lock 很像,實際調用的是 sync 實現類的 release 方法。和 lock 方法一樣,這個 release 方法在 AbstractQueuedSynchronizer 中,

  1. if (tryRelease(arg)) { 
  2.     Node h = head; 
  3.     if (h != null && h.waitStatus != 0) 
  4.         unparkSuccessor(h); 
  5.     return true
  6. return false

這個方法中會先執行 tryRelease,它的實現也在 AbstractQueuedSynchronizer 的子類 Sync 中,如果釋放鎖成功,那么則會通過 unparkSuccessor 方法找到隊列中第一個 waitStatus<0的線程進行喚醒。我們下面看一下 tryRelease 方法代碼:

tryRelease 方法是公平鎖和非公平鎖都公用的,在鎖釋放的時候,是沒有公平和非公平的說法的。

從代碼中可以看到,鎖最終被釋放的標椎是 state 的狀態為 0,在重入加鎖的情況下,需要重入解鎖相應的次數后,才能最終把鎖釋放,比如線程 A 對共享資源 B 重入加鎖 5 次,那么釋放鎖的話,也需要釋放 5 次之后,才算真正的釋放該共享資源了。

總結

本篇文章 ReentrantLock 的使用及其核心源代碼,其實 Lock 相關的代碼還有很多。我們可以嘗試自己去閱讀。

ReentrantLock 的設計思想是通過 FIFO 的隊列保存等待鎖的線程。通過 volatile 類型的 state 保存鎖的持有數量,從而實現了鎖的可重入性。而公平鎖則是通過判斷自己是否排隊成功,來決定是否去爭搶鎖。

然后我們了解到AQS 搭建了整個鎖架構,子類鎖只需要根據場景,實現 AQS 對應的方法即可,不僅僅是 ReentrantLock 是這樣,JUC 中的其它鎖也都是這樣,只要對 AQS 了如指掌,鎖其實非常簡單。

本文轉載自微信公眾號「月伴飛魚」,可以通過以下二維碼關注。轉載本文請聯系月伴飛魚公眾號。

 

責任編輯:武曉燕 來源: 日常加油站
相關推薦

2020-08-24 08:13:25

非公平鎖源碼

2022-11-14 11:09:36

源碼AQS加鎖

2020-09-16 07:43:44

Vue

2022-07-11 20:46:39

AQSJava

2023-04-06 00:15:03

JavaReentrantL線程

2023-09-12 13:48:47

2022-12-31 09:42:14

超時功能

2020-11-02 09:35:04

ReactHook

2020-12-03 08:14:45

Axios核心Promise

2019-08-05 13:20:35

Android繪制代碼

2022-12-26 00:00:04

公平鎖非公平鎖

2021-07-12 09:45:36

NameServer 核心Conusmer

2021-03-04 08:26:17

synchronizeReentrantLojava

2021-04-21 07:52:39

核心SignalR應用

2021-04-28 10:13:58

zookeeperZNode核心原理

2021-08-02 07:57:03

注冊Nacos源碼

2020-05-21 13:25:43

Spring組件架構

2021-10-12 09:46:00

Pipelineshell命令Jenkins

2023-05-08 14:56:00

Kafka高可靠高性能

2018-03-21 11:05:26

Spark大數據應用程序
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 九九热在线视频免费观看 | 日本在线视频中文字幕 | 久久精品亚洲成在人线av网址 | 91新视频| 中文字幕91av | 精品1区2区3区 | 99久久99久久精品国产片果冰 | 国产一级一片免费播放 | 国产偷录视频叫床高潮对白 | 国产精品一区二区三区久久久 | 亚洲国产一 | 一区二区三区四区免费视频 | 伊人精品久久久久77777 | 国产一区二区三区高清 | 久精品视频 | 国产亚洲精品久久19p | 国产精品美女久久久av超清 | 欧美婷婷 | av免费网站在线 | www.久久 | 亚洲成人午夜电影 | 中日字幕大片在线播放 | www.精品国产| 中文字幕亚洲欧美 | 久久久久久黄 | 国产 亚洲 网红 主播 | av在线一区二区三区 | 成人精品国产免费网站 | 色综合天天综合网国产成人网 | 精品国产乱码一区二区三 | 久色视频在线 | 国产精品视频久久 | 成人国产精品久久久 | 亚洲精品亚洲人成人网 | 久久免费国产 | 黄一级| 久久久久久网站 | 波霸ol一区二区 | 91九色在线观看 | 国产视频一区二区 | 久久精品视频在线观看 |