Java併發 ReentrantLock實現分析

2021-08-20 12:19:30 字數 4171 閱讀 2896

reentrantlock是基於aqs實現的可重入獨佔鎖,如果還不了解aqs實現原理的同學可以先去aqs原理分析學習一哈。

如果文章中由任何不妥或者謬誤之處,請批評指正。

2. 公平鎖

3. 解鎖過程

reentrantlock核心功能的實現,依賴於繼承aqs類實現的同步器。

reentrantlock有三個核心內部類,基於aqs實現的sync(同步器類),基於sync類實現的nonfairsync(非公平同步器)和fairsync(公平同步器)。

公平鎖呼叫了fairsync類的方法,公平鎖的含義是,多個執行緒依照依次嘗試去獲取鎖,而鎖釋放以後,等待執行緒按照先後順序獲取鎖。

非公平鎖的實現就是呼叫了nonfairsync類的方法,非公平鎖的含義就是,多個執行緒有時間順序的去嘗試獲取鎖,如果獲取失敗進入等待狀態,如果共享資源得到釋放,這些等待執行緒再去競爭獲取鎖(和嘗試獲取鎖的先後順序無關),所以是非公平的。

舉個通俗的例子就是,一群人去吃烤鴨,店主每次只能烤出乙隻烤鴨給客戶吃,公平鎖就是等著吃烤鴨的那些人,排起了長隊,而非公平鎖就是,等著的那群人不僅不排隊,還要互相推搡,爭搶著去吃烤鴨。

先講非公平鎖的原因是非公平鎖比較常用。reentrantlock無參的構造方法就會生成乙個非公平鎖。

public

reentrantlock()

我們來直接看一下nonfairsync的原始碼。

static

final

class

nonfairsync

extends

sync

protected

final

boolean tryacquire(int acquires)

}

我們重點來看快速獲取鎖失敗以後的操作。呼叫了acquire(int)方法,而根據aqs的原始碼,我們已經了解到,acquire(int)方法會呼叫tryacquire(int)方法,饒了一圈又回來了,眼皮子底下的tryacquire(int)其實是個皮包公司。

最終還是nonfairtryacquire(int)方法真實實現了非公平鎖。

nonfairtryacquire(int)又在哪呢?在nonfairsync父類裡!這裡不得不吐槽一句了,這種寫法十分不符合oo設計原則,估計是doug lea大神一開始壓根就沒想公平鎖這個東西,所以就順手寫下來了,至今還沒有人敢動過這些**,所以可見doug lea大神不是吾等小輩可以評價的。

我們來看一下nonfairtryacquire(int)是怎麼設計的。

final

boolean nonfairtryacquire(int acquires)

}// 下面是重入鎖的情況

else

if (current == getexclusiveownerthread())

// 如果不能獲取鎖就返回false

return

false;

}

這段**還是很容易理解的,而且邏輯也比較清晰,可以看到重入鎖的設計就是使用了aqs中的state狀態碼呀。

// aqs中的同步狀態碼就是這樣嬸兒滴

private

volatile

int state;

reentrantlock核心設計思路還是cas+volatile的機制,確保了執行緒安全,而通過只使用state狀態碼來標識執行緒狀態,比起使用作業系統的鎖機制需要上下文的切換,可以說是非常的節省資源了。(順便一提,目前為止安卓虛擬機器上reentrantlock的效率比使用synchronized關鍵字的效率要高的多)。

如果鎖獲取失敗,那麼就進入了我們熟悉的aqs流程。

public

final

void

acquire(int arg)

無論是addwaiter(node.exclusive)將當前執行緒包裝成乙個佇列節點,然後入隊,還是acquirequeued(node,arg)方法讓執行緒等待,並且等待喚醒以後競爭資源,這都是aqs為我們實現的方法,在此不做詳細解釋。

公平鎖比起非公平鎖效率低一些,因為非公平鎖有一些快速獲取鎖的機制,但是這種機制會導致插隊的情況發生,而如果沒有這些機制,就會需要等待佇列中的執行緒的被喚醒,這個過程由作業系統來安排,所以速度會慢一些。

我們直接來看fairsync類的實現。

static

final

class

fairsync

extends

sync

protected

final

boolean tryacquire(int acquires)

}// 鎖重入機制的實現,和非公平鎖一樣

else

if (current == getexclusiveownerthread())

return

false;

}}

fairsync類中的lock()方法中沒有快速獲得鎖的if語句,而是直接進入acquire(int)方法,這是第一點防止插隊機制,原理是防止有解鎖時,有未在佇列中的新建執行緒,直接獲得了鎖。

fairsync類中的tryacquire(int)nonfairsync類中的方法只有一處區別,就是判斷當前執行緒在佇列中是否有前驅節點,如果沒有前驅節點,才能獲取鎖。這是第二點防止插隊機制,原理是防止進入acquire(int)的執行緒,還沒有入隊,又反過頭來直接獲取了鎖。

這兩點不同於非公平鎖的機制保證了,多個執行緒會順序的進入等待佇列,最終做到了公平鎖的機制。

這個過程相對於獲取鎖的過程就簡單了許多。

public

void

unlock()

release(int)是aqs中的釋放資源的方法,也很簡單。

public

final

boolean

release(int arg)

return

false;

}

最終還是reentrantlock中的tryrelease方法在搞事情,我們觀察一下它。

protected

final

boolean

tryrelease(int releases)

setstate(c); // 修改state狀態碼

return free;

}

這個方法可以說十分的容易理解了。而state的增加和減少,實現了對於同一執行緒的鎖重入,獲取成功一次state就加1,釋放鎖成功一次就減1,為0時則說明鎖已被徹底釋放

reentrantlock實現原理深入**

深入剖析reentrantlock公平鎖與非公平鎖原始碼實現

java 併發 高併發概述

為什麼需要並行 有關並行的重要概念 有關並行效能的2個重要定律 多執行緒基礎 執行緒的基本操作 守護執行緒 優先順序 中斷處理 基本的執行緒同步操作 各種同步控制工具的使用 併發容器及典型原始碼分析 同步工具 併發容器使用小案例 第5課 jdk並發包 執行緒池的基本使用 擴充套件和增強執行緒池 執行...

JAVA併發程式設計

通過常量字串 string 來呼叫 wait 或 notify 方法所導致的問題是,jvm 編譯器會在內部自動將內容相同的 string 轉變為相同的物件。這意味著,即便你建立了兩個不同的 mywaitnotify 例項,他們內部的 mymonitorobject 變數也會指向相同的 string ...

Java併發學習

同步和非同步通常用來形容一次方法呼叫。同步方法呼叫一開始,呼叫者必須等待被呼叫的方法結束後,呼叫者後面的 才能執行。而非同步呼叫,指的是,呼叫者不用管被呼叫方法是否完成,都會繼續執行後面的 當被呼叫的方法完成後會通知呼叫者。比如,在超時購物,如果一件物品沒了,你得等倉庫人員跟你調貨,直到倉庫人員跟你...