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

