Java自定義互斥鎖的實(shí)現(xiàn)方法示例
今天我們利用AQS實(shí)現(xiàn)一個(gè)基礎(chǔ)的自定義的互斥鎖。
1.AQS
AbstractQueueSynchronizer,簡稱AQS,是 Java 并發(fā)編程的核心基礎(chǔ)框架,它為各種同步器(如ReentrantLock、Semaphore、CountDownLatch等)提供了統(tǒng)一的底層實(shí)現(xiàn),解決了并發(fā)場景中線程的同步與協(xié)作問題。它的設(shè)計(jì)思想可以概括為:用一個(gè) volatile 狀態(tài)變量控制同步邏輯,用一個(gè) CLH 隊(duì)列管理等待線程。下面我們簡單介紹一下其核心內(nèi)容。
一、AQS 的核心結(jié)構(gòu)
AQS 的核心由兩部分組成:同步狀態(tài)(State) 和等待隊(duì)列(CLH 隊(duì)列)。
1.同步狀態(tài)(State)
定義:AQS 通過 private volatile int state 變量來維護(hù)同步狀態(tài),該狀態(tài)值的具體語義由實(shí)現(xiàn)類(子類)自行定義。以 ReentrantLock 為例:
state = 0表示鎖未被占有state > 0表示鎖被持有,數(shù)值代表當(dāng)前線程的重入次數(shù)(線程每次獲取鎖時(shí) state 遞增,釋放鎖時(shí) state 遞減)
2. 等待隊(duì)列(CLH 隊(duì)列)
當(dāng)線程獲取鎖失敗時(shí),AQS 會(huì)將線程封裝成一個(gè)節(jié)點(diǎn)(Node) 加入等待隊(duì)列,這個(gè)隊(duì)列是一個(gè)雙向鏈表,基于 CLH(Craig, Landin, and Hagersten)鎖隊(duì)列改進(jìn)而來。
二、AQS 的核心機(jī)制
AQS(AbstractQueuedSynchronizer)的核心機(jī)制分為獨(dú)占式和共享式兩種同步模式,分別對(duì)應(yīng)不同的鎖獲取與釋放邏輯。
1.獨(dú)占式模式(Exclusive)
獨(dú)占式模式下,同一時(shí)刻只有一個(gè)線程能獲取鎖(如 ReentrantLock)。其核心流程如下:
獲取鎖(acquire)
調(diào)用 tryAcquire(int arg):由子類實(shí)現(xiàn),嘗試獲取鎖(如檢查 state 是否為 0,并通過 CAS 修改 state)。若成功,直接返回;若失敗,進(jìn)入后續(xù)步驟。
調(diào)用 addWaiter(Node.EXCLUSIVE):將當(dāng)前線程封裝為獨(dú)占模式的 Node 節(jié)點(diǎn),并加入等待隊(duì)列尾部。
調(diào)用 acquireQueued(Node node, int arg):節(jié)點(diǎn)在隊(duì)列中自旋等待,直到獲取鎖或被取消。在此過程中:
- 節(jié)點(diǎn)檢查前驅(qū)節(jié)點(diǎn)是否為頭節(jié)點(diǎn):若是,則再次嘗試
tryAcquire;若成功,將自己設(shè)為新頭節(jié)點(diǎn)。 - 若前驅(qū)節(jié)點(diǎn)狀態(tài)不是
SIGNAL,則將其狀態(tài)改為SIGNAL(確保前驅(qū)釋放鎖時(shí)能喚醒自己)。 - 若獲取失敗,通過
LockSupport.park(this)阻塞當(dāng)前線程。
釋放鎖(release)
調(diào)用 tryRelease(int arg):由子類實(shí)現(xiàn),嘗試釋放鎖(如修改 state 的值)。
若成功,檢查頭節(jié)點(diǎn)的狀態(tài):若為 SIGNAL,則調(diào)用 unparkSuccessor(Node node) 喚醒后繼節(jié)點(diǎn)。
被喚醒的后繼節(jié)點(diǎn)會(huì)重新進(jìn)入自旋,嘗試獲取鎖。
2.共享式模式(Shared)
共享式模式下,同一時(shí)刻多個(gè)線程可獲取鎖(如 Semaphore、CountDownLatch)。其核心流程如下:
獲取鎖(acquireShared)
調(diào)用 tryAcquireShared(int arg):由子類實(shí)現(xiàn),嘗試獲取共享鎖(如檢查 state 是否大于 0)。
若返回值 >=0(成功),直接返回;若 <0(失?。M(jìn)入后續(xù)步驟。
調(diào)用 doAcquireShared(int arg):將線程封裝為共享模式的 Node 節(jié)點(diǎn),加入等待隊(duì)列并自旋等待。
與獨(dú)占式不同,共享模式下,一個(gè)節(jié)點(diǎn)獲取鎖后可能會(huì)喚醒后續(xù)所有共享節(jié)點(diǎn)(如 Semaphore 的 release 會(huì)釋放多個(gè)許可)。
釋放鎖(releaseShared)
調(diào)用 tryReleaseShared(int arg):由子類實(shí)現(xiàn),嘗試釋放共享鎖(如增加 state 的值)。
若成功,喚醒后續(xù)等待的共享節(jié)點(diǎn)。
三、AQS 的核心方法
AQS 基于模板方法模式設(shè)計(jì),將同步器的核心邏輯分為兩部分:父類定義的固定流程(模板方法)和子類實(shí)現(xiàn)的定制邏輯(鉤子方法)。
1.模板方法(AQS 已實(shí)現(xiàn))
獨(dú)占式鎖操作
acquire(int arg):阻塞式獲取獨(dú)占鎖,忽略中斷。release(int arg):釋放獨(dú)占鎖,喚醒后續(xù)線程。tryAcquireNanos(int arg, long nanosTimeout):支持超時(shí)和中斷的獨(dú)占鎖獲取。
共享式鎖操作
acquireShared(int arg):阻塞式獲取共享鎖。releaseShared(int arg):釋放共享鎖,喚醒等待線程。tryAcquireSharedNanos(int arg, long nanosTimeout):支持超時(shí)和中斷的共享鎖獲取。
輔助功能
getQueuedThreads():獲取等待隊(duì)列中的線程集合。
2.鉤子方法(子類需重寫)
獨(dú)占式鎖實(shí)現(xiàn)
tryAcquire(int arg):定義獨(dú)占鎖的獲取邏輯,返回成功狀態(tài)。tryRelease(int arg):定義獨(dú)占鎖的釋放邏輯,返回成功狀態(tài)。
共享式鎖實(shí)現(xiàn)
tryAcquireShared(int arg):定義共享鎖的獲取邏輯,返回剩余可用資源數(shù)。tryReleaseShared(int arg):定義共享鎖的釋放邏輯,返回是否完全釋放。
條件變量支持
isHeldExclusively():判斷當(dāng)前線程是否獨(dú)占持有鎖,用于實(shí)現(xiàn) Condition。
通過重寫鉤子方法,子類可靈活實(shí)現(xiàn)公平/非公平鎖、讀寫鎖等同步機(jī)制,而 AQS 負(fù)責(zé)隊(duì)列管理、阻塞喚醒等底層操作。
2.自定義互斥鎖
MLock通過靜態(tài)內(nèi)部類Sync繼承 AQS,并重寫關(guān)鍵方法,實(shí)現(xiàn)了一個(gè)獨(dú)占式非重入鎖(同一線程不能重復(fù)獲取鎖)。
public class MLock {
private Sync sync = new Sync();
private static class Sync extends AbstractQueuedLongSynchronizer{
@Override
protected boolean tryAcquire(long arg) {
//利用CAS算法把state變量改成1
if(compareAndSetState(0,arg)){
//操作成功后把當(dāng)前線程設(shè)置成獨(dú)占
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(long arg) {
//清空當(dāng)前線程
setExclusiveOwnerThread(null);
setState(arg);
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
//加鎖
public void lock(){
sync.acquire(1);
}
//釋放鎖
public void unlock(){
sync.release(0);
}
}
一、Sync內(nèi)部類(AQS 子類)
Sync 作為鎖的核心實(shí)現(xiàn),重寫了 AQS 中的 3 個(gè)關(guān)鍵方法和 1 個(gè)條件變量方法:
1.tryAcquire(int arg) - 獨(dú)占鎖獲取
執(zhí)行邏輯:當(dāng) state 為 0(未鎖定狀態(tài))時(shí),通過 CAS 操作將其置為 1,并記錄當(dāng)前線程為鎖持有者
2.tryRelease(int arg) - 獨(dú)占鎖釋放
執(zhí)行邏輯:先清除鎖持有者線程信息,再將 state 重置為 0,允許其他線程獲取鎖
3.isHeldExclusively() - 獨(dú)占鎖狀態(tài)檢查
執(zhí)行邏輯:通過檢查 state 是否為 1 來判斷鎖是否被占用(注:簡化實(shí)現(xiàn),實(shí)際還需驗(yàn)證持有者線程)
4.newCondition() - 條件變量創(chuàng)建
功能說明:提供基于鎖的等待/通知機(jī)制,支持 await() 和 signal() 等操作
二、MLock對(duì)外提供的方法
這些方法是鎖的使用接口,內(nèi)部通過調(diào)用Sync(AQS)的方法實(shí)現(xiàn):
lock():獲取鎖(阻塞式)邏輯:如果
tryAcquire成功(獲取鎖),直接返回;否則,當(dāng)前線程會(huì)被加入 AQS 的等待隊(duì)列,進(jìn)入阻塞狀態(tài),直到被喚醒并成功獲取鎖tryLock():嘗試獲取鎖(非阻塞式)
邏輯:僅嘗試一次獲取鎖,成功返回true,失敗立即返回false,不會(huì)阻塞。unlock():釋放鎖邏輯:釋放鎖后,AQS 會(huì)喚醒等待隊(duì)列中的一個(gè)線程,讓其嘗試獲取鎖。
newCondition():獲取條件變量用于線程間的協(xié)作(如生產(chǎn)者 - 消費(fèi)者模型),基于當(dāng)前鎖實(shí)現(xiàn)等待 / 通知。
總結(jié)
到此這篇關(guān)于Java自定義互斥鎖實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)Java自定義互斥鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解SpringBoot通用配置文件(不定時(shí)更新)
這篇文章主要介紹了SpringBoot通用配置文件詳解(不定時(shí)更新),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
Spring中獲取HttpServletRequest的三種方式小結(jié)
spring框架web環(huán)境中,獲取HttpServletRequest是常見的操作,本文將為大家詳細(xì)介紹一下Spring中獲取HttpServletRequest的三種方式,有需要的小伙伴可以了解下2026-04-04
maven插件spring-boot-starter-tomcat的使用方式
這篇文章主要介紹了maven插件spring-boot-starter-tomcat的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
spring boot環(huán)境抽象的實(shí)現(xiàn)方法
在實(shí)際開發(fā)中,開發(fā)人員在編寫springboot的時(shí)候通常要在本地環(huán)境測試然后再部署到Production環(huán)境,這兩種環(huán)境一般來講是不同的,最主要的區(qū)別就是數(shù)據(jù)源的不同。本文主要介紹了這兩種,感興趣的可以了解一下2019-04-04
關(guān)于ArrayList初始創(chuàng)建設(shè)定長度問題
在使用ArrayList時(shí),初始化長度并不等同于直接設(shè)定數(shù)組大小,如通過構(gòu)造函數(shù)指定長度,僅僅是在內(nèi)部開辟了相應(yīng)的存儲(chǔ)空間,并不會(huì)改變ArrayList的實(shí)際元素個(gè)數(shù),即size屬性仍然為0,因此,嘗試直接訪問未實(shí)際添加元素的位置會(huì)引發(fā)異常2024-11-11
Spring中@Autowired注解作用在方法上和屬性上說明
這篇文章主要介紹了Spring中@Autowired注解作用在方法上和屬性上說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11

