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

