java之AQS(AbstractQueuedSynchronizer)用法解讀
AQS(AbstractQueuedSynchronizer)是Java并發(fā)包(java.util.concurrent)中的一個(gè)核心組件,是構(gòu)建鎖和其他同步器的基礎(chǔ)框架。
以下是對(duì)AQS的詳細(xì)理解:
一、AQS的基本概念
AQS,全稱(chēng)為AbstractQueuedSynchronizer,是一個(gè)抽象的隊(duì)列式同步器。
它定義了一套多線程訪問(wèn)共享資源的同步器框架,為Java并發(fā)同步組件提供統(tǒng)一的底層支持。
AQS是一個(gè)為各個(gè)同步組件提供基本框架的抽象類(lèi),其內(nèi)部實(shí)現(xiàn)了同步狀態(tài)的管理和線程的排隊(duì)機(jī)制。
二、AQS的核心組件
- 同步狀態(tài):AQS使用一個(gè)
int類(lèi)型的成員變量來(lái)表示同步狀態(tài)。這個(gè)狀態(tài)變量是線程共享的資源,通過(guò)內(nèi)置的FIFO隊(duì)列(先進(jìn)先出隊(duì)列)來(lái)完成獲取資源線程的排隊(duì)工作。 - FIFO隊(duì)列:AQS的底層實(shí)現(xiàn)是一個(gè)雙向鏈表,用于管理等待獲取同步狀態(tài)的線程。當(dāng)線程無(wú)法獲取同步狀態(tài)時(shí),會(huì)被加入到這個(gè)隊(duì)列中等待。
- CLH隊(duì)列鎖:AQS使用CLH隊(duì)列鎖來(lái)實(shí)現(xiàn)線程的阻塞等待和喚醒機(jī)制。CLH是一個(gè)虛擬的雙向隊(duì)列,即不存在隊(duì)列實(shí)例,僅存在節(jié)點(diǎn)之間的關(guān)聯(lián)關(guān)系。
三、AQS的工作原理
- 獲取同步狀態(tài):當(dāng)線程嘗試獲取同步狀態(tài)時(shí),會(huì)調(diào)用AQS的
tryAcquire方法(對(duì)于獨(dú)占模式)或tryAcquireShared方法(對(duì)于共享模式)。如果成功獲取到同步狀態(tài),則返回true;否則,線程會(huì)被加入到等待隊(duì)列中。 - 釋放同步狀態(tài):當(dāng)線程釋放同步狀態(tài)時(shí),會(huì)調(diào)用AQS的
tryRelease方法(對(duì)于獨(dú)占模式)或tryReleaseShared方法(對(duì)于共享模式)。這些方法會(huì)修改同步狀態(tài)的值,并可能喚醒等待隊(duì)列中的線程。 - 線程排隊(duì)與喚醒:等待獲取同步狀態(tài)的線程會(huì)被加入到FIFO隊(duì)列中,并按照隊(duì)列的順序等待。當(dāng)某個(gè)線程釋放同步狀態(tài)時(shí),會(huì)喚醒隊(duì)列中的下一個(gè)線程來(lái)嘗試獲取同步狀態(tài)。
四、AQS的資源共享方式
AQS支持兩種資源共享方式:獨(dú)占(Exclusive)和共享(Share)。
- 獨(dú)占:只有一個(gè)線程能執(zhí)行,如ReentrantLock。獨(dú)占鎖又可分為公平鎖和非公平鎖。公平鎖按照線程在隊(duì)列中的排隊(duì)順序來(lái)獲取鎖,非公平鎖則允許線程無(wú)視隊(duì)列順序直接去搶鎖。
- 共享:多個(gè)線程可同時(shí)執(zhí)行,如Semaphore、CountDownLatch、CyclicBarrier等。共享鎖允許多個(gè)線程同時(shí)訪問(wèn)共享資源。
五、AQS的應(yīng)用場(chǎng)景
AQS廣泛應(yīng)用于Java并發(fā)編程中,是實(shí)現(xiàn)各種同步機(jī)制的基礎(chǔ)。
例如,ReentrantLock、Semaphore、CountDownLatch等同步器都是基于AQS實(shí)現(xiàn)的。
通過(guò)擴(kuò)展AQS,開(kāi)發(fā)者可以實(shí)現(xiàn)各種復(fù)雜的同步器,以滿足不同的并發(fā)編程需求。
六、AQS的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 提供了統(tǒng)一的同步器框架,簡(jiǎn)化了同步器的實(shí)現(xiàn)過(guò)程。
- 使用了FIFO隊(duì)列來(lái)管理等待線程,保證了線程的公平性。
- 提供了靈活的資源共享方式,支持獨(dú)占和共享兩種模式。
缺點(diǎn):
- AQS是一個(gè)相對(duì)復(fù)雜的框架,需要開(kāi)發(fā)者對(duì)其內(nèi)部機(jī)制有一定的了解才能正確使用。
- 在某些情況下,AQS的性能可能不如一些定制的同步器。
七、AQS示例
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
// 自定義鎖類(lèi)
class MyLock implements Lock {
// 靜態(tài)內(nèi)部類(lèi),繼承AQS
private static class Sync extends AbstractQueuedSynchronizer {
// 是否處于獨(dú)占模式
protected boolean isHeldExclusively() {
return getState() == 1;
}
// 嘗試獲取鎖,當(dāng)狀態(tài)為0時(shí)獲取鎖成功
public boolean tryAcquire(int acquires) {
assert acquires == 1; // 只允許獲取1個(gè)單位的鎖
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 嘗試釋放鎖,將狀態(tài)設(shè)置為0
protected boolean tryRelease(int releases) {
assert releases == 1; // 只允許釋放1個(gè)單位的鎖
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// 提供條件變量
Condition newCondition() { return new ConditionObject(); }
}
// 將操作代理到Sync上
private final Sync sync = new Sync();
// 實(shí)現(xiàn)Lock接口的方法
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
public static void main(String[] args) {
MyLock lock = new MyLock();
Runnable task = () -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 獲取鎖");
// 模擬任務(wù)執(zhí)行
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
} finally {
System.out.println(Thread.currentThread().getName() + " 釋放鎖");
lock.unlock();
}
};
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
t1.start();
t2.start();
}
}MyLock類(lèi):
- 實(shí)現(xiàn)
Lock接口,內(nèi)部包含一個(gè)靜態(tài)內(nèi)部類(lèi)Sync,該內(nèi)部類(lèi)繼承自AbstractQueuedSynchronizer。
Sync類(lèi):
isHeldExclusively:判斷當(dāng)前線程是否持有鎖。tryAcquire:嘗試獲取鎖,如果當(dāng)前狀態(tài)為0(表示鎖未被持有),則通過(guò)compareAndSetState方法將狀態(tài)設(shè)置為1,并設(shè)置當(dāng)前線程為獨(dú)占線程。tryRelease:嘗試釋放鎖,將狀態(tài)設(shè)置為0,并清除獨(dú)占線程。newCondition:創(chuàng)建一個(gè)條件變量。
MyLock方法:
- 將
Lock接口的方法代理到Sync對(duì)象上。
main方法:
- 創(chuàng)建兩個(gè)線程,每個(gè)線程嘗試獲取和釋放鎖,模擬任務(wù)執(zhí)行。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java圖形界面開(kāi)發(fā)之簡(jiǎn)易記事本
這篇文章主要為大家詳細(xì)介紹了Java圖形界面開(kāi)發(fā)之簡(jiǎn)易記事本的制作方法,,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
使用java代碼獲取新浪微博應(yīng)用的access token代碼實(shí)例
這篇文章主要介紹了使用java代碼獲取新浪微博應(yīng)用的access token實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
Java中常用輸出方式(print() println() printf())
這篇文章主要介紹了Java中常用輸出方式(print() println() printf()),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
SpringBoot項(xiàng)目讀取外置logback配置文件的問(wèn)題及解決
SpringBoot項(xiàng)目讀取外置logback配置文件的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
Java基礎(chǔ)之MapReduce框架總結(jié)與擴(kuò)展知識(shí)點(diǎn)
本章,是MapReduce的最終章,我在寫(xiě)本章的時(shí)候,發(fā)現(xiàn)前面忘記介紹MpaTask與ReduceTask了,所以本章補(bǔ)上哈,另外還有兩個(gè)擴(kuò)展的知識(shí)點(diǎn),講完這些,我會(huì)對(duì)整個(gè)MapReduce進(jìn)行總結(jié)一下,讓大家再次了解MapReduce的工作流程,更加清晰地認(rèn)識(shí)MapReduce ,需要的朋友可以參考下2021-05-05

