java?Semaphore共享鎖實(shí)現(xiàn)原理解析
正文
在線程間通信方式中,我們了解到可以使用Semaphore信號(hào)量來實(shí)現(xiàn)線程間通信,Semaphore支持公平鎖和非公平鎖,Semaphore底層是通過共享鎖來實(shí)現(xiàn)的,其支持兩種構(gòu)造函數(shù),如下所示:
// 默認(rèn)使用非公平鎖實(shí)現(xiàn)
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
?
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
Semaphore提供的常用函數(shù)如下所示:
| 函數(shù)名 | 說明 | 備注 |
|---|---|---|
| acquire | 獲取鎖 | / |
| release | 釋放鎖 | / |
下面我們來看下Semaphore內(nèi)部的實(shí)現(xiàn)原理
Semaphore內(nèi)部類及繼承關(guān)系

可以看出Semaphore和ReentrantLock實(shí)現(xiàn)原理基本一致,包含NonfairSync和FairSync兩個(gè)內(nèi)部類,這兩個(gè)內(nèi)部類的父類均為AQS,不妨大膽猜測(cè)Semaphore也是依賴AQS實(shí)現(xiàn)的,接下來我們一起來看下Semaphore獲取和釋放鎖的流程。
Semaphore.acquire流程分析(以非公平鎖為例)

從上圖可以看出,針對(duì)阻塞線程的部分實(shí)現(xiàn),和ReentrantLock基本一致,我們不做贅述,主要來看下前半部分的源碼實(shí)現(xiàn):
// Semaphore.java
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// AbstractQueuedSynchronizer.java
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
// 如果線程是中斷狀態(tài),拋出異常
if (Thread.interrupted())
throw new InterruptedException();
// 嘗試獲取共享資源
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
從源碼可以看出acquire主要依賴于tryAcquireShared和doAcquireSharedInterruptibly,接下來我們來分別看下這兩塊的代碼
tryAcquireShared
// NonfairSync
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
// Sync
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
// AbstractQueuedSynchronizer.java
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
從代碼可以看出這里主要是根據(jù)申請(qǐng)的許可證數(shù)量,比較時(shí)否有許可證數(shù)量,如果可用許可證數(shù)量小于0,則直接返回,如果大于0,則通過CAS將state設(shè)置為可用許可證數(shù)量。
doAcquireSharedInterruptibly
當(dāng)tryAcquireShared中返回的可用許可證數(shù)量小于0時(shí),執(zhí)行doAcquireSharedInterruptibly流程,代碼如下:
// AbstractQueuedSynchronizer.java
// 在隊(duì)尾新建Node對(duì)象并添加
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
// AbstractQueuedSynchronizer.java
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
// 將當(dāng)前線程添加到等待隊(duì)列
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
// for循環(huán)自旋
for (;;) {
// 獲取node的前一個(gè)節(jié)點(diǎn)
final Node p = node.predecessor();
// 如果前一個(gè)節(jié)點(diǎn)是頭節(jié)點(diǎn)
if (p == head) {
// 嘗試獲取鎖
int r = tryAcquireShared(arg);
if (r >= 0) {
// 獲取鎖成功,更新node信息設(shè)置為頭節(jié)點(diǎn),并通知其他節(jié)點(diǎn)
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
// 判斷是否需要阻塞線程,設(shè)置waitStatus并阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
執(zhí)行setHeadAndPropagate的主要目的在于,這里能獲取到說明在該線程自旋過程中有線程釋放了許可證,釋放的許可證數(shù)量有可能還有剩余,所以傳遞給其他節(jié)點(diǎn)的線程,喚醒其他阻塞狀態(tài)的線程也嘗試去獲取許可證。
Semaphore.release流程分析(以非公平鎖為例)

Semaphore.release流程相對(duì)而言,就比較簡(jiǎn)單,將release傳遞到AQS內(nèi)部通過CAS更新許可證數(shù)量信息,更新完成后,遍歷隊(duì)列中Node節(jié)點(diǎn),將Node waitStatus設(shè)置為0,并對(duì)對(duì)應(yīng)線程執(zhí)行unpark,相關(guān)代碼如下:
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
// 通過CAS更新許可證數(shù)量
if (compareAndSetState(current, next))
return true;
}
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
// 許可證數(shù)量更新完成后,調(diào)用該方法喚醒線程
private void doReleaseShared() {
// 自旋
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
// 喚醒后繼節(jié)點(diǎn)線程搶占許可證
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
綜上,我們分析了Smaphore非公平鎖的實(shí)現(xiàn),感興趣的可以分析下公平鎖的實(shí)現(xiàn),其本質(zhì)區(qū)別在于在tryAcquireShared中只有當(dāng)?shù)却?duì)列為空時(shí),才會(huì)去嘗試更新剩余許可證數(shù)量。
以上就是Semaphore共享鎖實(shí)現(xiàn)原理解析的詳細(xì)內(nèi)容,更多關(guān)于Semaphore共享鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
利用Mybatis向PostgreSQL中插入并查詢JSON字段
這篇文章主要介紹了利用Mybatis向PostgreSQL中插入并查詢JSON字段,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-07-07
springboot配置flyway(入門級(jí)別教程)
本文介紹了springboot配置flyway,主要介紹基于SpringBoot集成flyway來管理數(shù)據(jù)庫(kù)的變更,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
SpringBoot父子線程數(shù)據(jù)傳遞的五種方案介紹
在實(shí)際開發(fā)過程中我們需要父子之間傳遞一些數(shù)據(jù),比如用戶信息等。該文章從5種解決方案解決父子之間數(shù)據(jù)傳遞困擾,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-09-09
新手入門學(xué)習(xí)Spring Freemarker教程解析
這篇文章主要介紹了新手入門學(xué)習(xí)Freemarker教程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
Java通過notify和wait實(shí)現(xiàn)線程間的通信功能
在軟件開發(fā)中,線程是實(shí)現(xiàn)并發(fā)執(zhí)行的重要手段,然而,線程之間的協(xié)作與通信卻是開發(fā)者必須重點(diǎn)考慮的挑戰(zhàn)之一,Java作為一種廣泛應(yīng)用于多線程編程的語言,本文將深入探討Java中通過notify和wait實(shí)現(xiàn)線程間通信的機(jī)制,需要的朋友可以參考下2024-06-06
詳解Elastic Search搜索引擎在SpringBoot中的實(shí)踐
本篇文章主要介紹了Elastic Search搜索引擎在SpringBoot中的實(shí)踐,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01
Java 數(shù)據(jù)結(jié)構(gòu)哈希算法之哈希桶方式解決哈希沖突
實(shí)際上哈希桶是解決哈希表沖突的一種方法。常見的解決沖突的兩種方法:分離鏈接法、開放定址法。其中使用分離鏈接法,得到的對(duì)應(yīng)關(guān)系即為哈希桶2022-02-02

