網(wǎng)站首頁 編程語言 正文
深入理解AQS
- 一,AQS
- 1,ReentrantLock
- 2,CAS
- 3,AbstractQueuedSynchronizer
- 3.1,F(xiàn)airSync
- 3.2,NofairSync
- 3.3,AQS中幾個重要的相關(guān)參數(shù)
- 3.4,Node
一,AQS
AbstractQueuedSynchronizer,定義了一套多線程訪問共享資源的同步器框架,依賴于狀態(tài)的同步器
1,ReentrantLock
一種基于AQS框架的應(yīng)用實(shí)現(xiàn),類似于synchronized是一種互斥鎖,可以保證線程安全。它支持手動加鎖與解鎖,支持加鎖的公平性。主要是Lock鎖的實(shí)現(xiàn)
public class ReentrantLock implements Lock, Serializable
接下來可以手動的猜想一下這個reentrantLock的實(shí)現(xiàn)
ReentrantLock lock = new ReentrantLock(true);
HashSet hashSet = new HashSet();
3個線程 T0,T1,T2
lock.lock(); //加鎖
while(true){ //循環(huán),輪詢獲取鎖
if(加鎖成功){ //synchronized,cas cas:compare and swap
break;//跳出循環(huán)
}
//Thread.yeild() //讓出cpu使用權(quán)
//Thread.sleep(1000); //睡眠
hashSet.add(thread); //將當(dāng)前線程加入到set中
//阻塞
LockSupprot.park();
}
T0獲取鎖
xxxxx業(yè)務(wù)邏輯
xxxxx業(yè)務(wù)邏輯
lock.unlock();
Thread thread = hashSet.get();
//喚醒當(dāng)前線程,notify是喚醒隨機(jī)的線程
LockSupport.unPark(thread);
三大核心:自旋,加鎖,LockSupport,隊(duì)列(LinkQueue),為了解決這個公平鎖和非公平鎖,因此優(yōu)先考慮這個隊(duì)列。
2,CAS
compare and swap,比較與交換
如在jmm模型中,兩個工作內(nèi)存都去修改主內(nèi)存的值。主內(nèi)存中存在一個a = 0,線程A和線程B的工作內(nèi)存同時獲取到這個值,如果線程A先修改這個值,則線程A會和主內(nèi)存比較,如果線程A的值a和主內(nèi)存的值一致,那么就會直接進(jìn)行修改,如改成a = 1,那么線程B也要改這個值,線程B中的a = 0,那么會和主內(nèi)存a比較,發(fā)現(xiàn)不一致,主內(nèi)存a=1,那么就會優(yōu)先將線程B中的值修改成a = 1,再對a進(jìn)行修改。就是說相等直接修改,不相等需要重新讀取,再進(jìn)行修改。即在一個原子操作里面進(jìn)行比較和替換
主要通過這個unsafe類實(shí)現(xiàn),里面的實(shí)現(xiàn)也是原子類操作,主要是通過以下三個類實(shí)現(xiàn)。
//對象類型
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
//整型值
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
//Long型值
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
3,AbstractQueuedSynchronizer
該類是ReentrantLock里面的一個抽象內(nèi)部類。ctrl + alt + shift + u看所有子類,Ctrl + T,看所有的繼承類,可以發(fā)現(xiàn)很多地方都繼承或者實(shí)現(xiàn)了這個抽象類
Sync是ReentrantLock的一個抽象的靜態(tài)內(nèi)部類,根據(jù)圖也可以發(fā)現(xiàn)這個Sync繼承了這個AQS
abstract static class Sync extends AbstractQueuedSynchronizer
通過實(shí)現(xiàn)Sync這個接口得到了FairSync公平鎖類和NofairSync非公平鎖這個類
3.1,F(xiàn)airSync
實(shí)現(xiàn)了公平鎖,需要排隊(duì)獲取鎖,如存在線程t1,t2,t3依次獲取鎖,需要依次排隊(duì)執(zhí)行,突然來了一個線程t4,也是需要排在線程t3后面
ReentrantLock reentrantLock = new ReentrantLock(true);
public ReentrantLock(boolean fair) {
//入?yún)ⅲ糜谂袛嗍枪芥i還是非公平鎖
sync = fair ? new FairSync() : new NonfairSync();
}
//公平鎖的實(shí)現(xiàn)
static final class FairSync extends ReentrantLock.Sync{...}
公平鎖獲取鎖的方式如下
final void lock() {
acquire(1);
}
public final void acquire(int arg) {
//tryAcquire:嘗試去獲取鎖
//addWaiter:線程入隊(duì),一個同步等待隊(duì)列,基于雙向列表實(shí)現(xiàn)
//鏈表中的每一個結(jié)點(diǎn)為一個Node結(jié)點(diǎn),
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//線程入隊(duì)操作
private Node addWaiter(Node mode) {
//獲取當(dāng)前線程結(jié)點(diǎn)
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;
}
}
//結(jié)點(diǎn)入隊(duì)
enq(node);
return node;
}
//判斷是否獲取鎖
protected final boolean tryAcquire(int acquires) {
//獲取當(dāng)前獲取鎖的線程
final Thread current = Thread.currentThread();
//獲取當(dāng)前同步器狀態(tài),被volatile修飾的整型值,默認(rèn)為0
int c = getState();
//如果同步狀態(tài)器的值為0,說明外面的線程可以進(jìn)行加鎖操作
if (c == 0) {
//hasQueuedPredecessors:判斷隊(duì)列中是否還有在排隊(duì)的節(jié)點(diǎn)
//compareAndSetState:原子操作,比較與交換,進(jìn)行加鎖的操作,將state變量將0變?yōu)?
//setExclusiveOwnerThread:設(shè)置獲取鎖的的線程擁有者
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果狀態(tài)同步器不為0,可能由自己持有,也可能由別的線程持有鎖
//重復(fù)加鎖,如定義一個全局鎖,出現(xiàn)了這個可重入鎖的問題
else if (current == getExclusiveOwnerThread()) {
//如果自己持有鎖的話,state+1即可,反正不等于0就可以
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//別的線程獲有鎖,直接返回
return false;
}
總而言之就是說,公平鎖就是就是通過一個隊(duì)列實(shí)現(xiàn),需要進(jìn)行排隊(duì)的去獲取鎖資源。主要是通過這個state的資源狀態(tài)器來控制獲取鎖的擁有者,如果state為0,則表示隊(duì)列中的下一個線程可以去獲取鎖,并且通過cas的方式來保證鎖的安全并發(fā)問題。通過隊(duì)列的思想,來保證這個獲取鎖的公平性和有序性。
3.2,NofairSync
實(shí)現(xiàn)了非公平鎖,默認(rèn)為非公平鎖,會存在搶鎖的情況
ReentrantLock reentrantLock = new ReentrantLock(flase);
//如果不傳入?yún)?shù),默認(rèn)是非公平鎖
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
//入?yún)ⅲ糜谂袛嗍枪芥i還是非公平鎖
sync = fair ? new FairSync() : new NonfairSync();
}
//非公平鎖的實(shí)現(xiàn)
static final class NonfairSync extends ReentrantLock.Sync{...}
非公平鎖獲取鎖的方法和公平鎖類似,只是少了幾步使用隊(duì)列的幾個方法
3.3,AQS中幾個重要的相關(guān)參數(shù)
exclusiveOwnerThread:用于記錄當(dāng)前獨(dú)占模式下,獲取鎖的線程是誰
state:同步狀態(tài)器,默認(rèn)為0,表示當(dāng)前沒有線程獲取鎖,外面的線程可以來獲取鎖了
Node:雙向鏈表結(jié)構(gòu),是一個同步等待隊(duì)列,head:隊(duì)頭,tail:隊(duì)尾,prev前軀指針,next,后繼指針
waiteState:結(jié)點(diǎn)的什生命狀態(tài)
Lock鎖和synchronized鎖都是可重入鎖
3.4,Node
pre:前驅(qū)指針
next:后繼指針
waitStatues:每個結(jié)點(diǎn)都存在很多狀態(tài),這個主要是存儲結(jié)點(diǎn)的生命狀態(tài)
結(jié)點(diǎn)的幾個生命狀態(tài)如下
SIGNAL:-1 //可被喚醒
CANCELLED:1 //代表異常,中斷引起的,需要被廢棄
CONDITION:-2 //條件等待
PROPAGATE:-3 //傳播
Init:0初始狀態(tài)
結(jié)點(diǎn)入隊(duì)順序如下,入隊(duì)時,將入隊(duì)結(jié)點(diǎn)得前驅(qū)指針指向鏈表的tail結(jié)點(diǎn),將tail節(jié)點(diǎn)的next節(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn),并將當(dāng)前結(jié)點(diǎn)設(shè)置為tail尾指針結(jié)點(diǎn)。
private Node enq(final Node node) {
//自旋
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
//如果隊(duì)列為空,要防止出現(xiàn)多個線程的并發(fā)問題,結(jié)點(diǎn)直接放在隊(duì)頭
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
//保證這個入隊(duì)的安全性,防止入隊(duì)出現(xiàn)并發(fā)問題
//結(jié)點(diǎn)入隊(duì)到隊(duì)尾
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
當(dāng)前結(jié)點(diǎn)前面有結(jié)點(diǎn)獲取鎖,當(dāng)前節(jié)點(diǎn)需要進(jìn)行阻塞park,線程要開始排隊(duì)等待。
結(jié)點(diǎn)在阻塞之前,還得嘗試獲取一次鎖。
a,如果結(jié)點(diǎn)可以獲取到鎖,即當(dāng)前節(jié)點(diǎn)為頭結(jié)點(diǎn)的下一個結(jié)點(diǎn),頭結(jié)點(diǎn)即將鎖被釋放,則把當(dāng)前結(jié)點(diǎn)作為頭結(jié)點(diǎn)。之前的頭結(jié)點(diǎn)就可以被GC回收了
b,如果結(jié)點(diǎn)不能獲取到鎖,那么當(dāng)前結(jié)點(diǎn)就要等待被喚醒
? (1),第一輪循環(huán)會去修改head狀態(tài),并且將waitState修改為sinal = -1可被喚醒狀態(tài)
? (2),第二輪,阻塞線程
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//當(dāng)前節(jié)點(diǎn)的前驅(qū)指針指向的節(jié)點(diǎn)
final Node p = node.predecessor();
//如果當(dāng)前結(jié)點(diǎn)指向的該結(jié)點(diǎn)為頭部結(jié)點(diǎn),則不需要進(jìn)行阻塞
if (p == head && tryAcquire(arg)) {
//將當(dāng)前結(jié)點(diǎn)作為頭部結(jié)點(diǎn)
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
每個結(jié)點(diǎn)的生命狀態(tài)的變換如下。默認(rèn)的結(jié)點(diǎn)狀態(tài)為0,需要將節(jié)點(diǎn)狀態(tài)(waitState)轉(zhuǎn)化為-1可喚醒狀態(tài)。前一個節(jié)點(diǎn)中的waitStatus狀態(tài)記錄著后一個節(jié)點(diǎn)的生命狀態(tài)。
//pred:前驅(qū)節(jié)點(diǎn) node:當(dāng)前節(jié)點(diǎn)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//默認(rèn)為初始狀態(tài)0
int ws = pred.waitStatus;
//SIGNAL為-1,可被喚醒狀態(tài)
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//將頭結(jié)點(diǎn)初始狀態(tài)轉(zhuǎn)化為可喚醒狀態(tài)
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
在修改成可被喚醒的狀態(tài)之后,就進(jìn)行阻塞操作。
private final boolean parkAndCheckInterrupt() {
//阻塞線程,并且判斷該線程是否是被中斷的
LockSupport.park(this);
return Thread.interrupted();
}
在頭結(jié)點(diǎn)釋放鎖的時候,也會發(fā)一個通知去告知下一個需要獲取鎖的線程來搶鎖,即喚醒隊(duì)列中的下一個被阻塞的線程
//真正的釋放鎖
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//嘗試釋放鎖
if (tryRelease(arg)) {
Node h = head;
//因?yàn)榍耙粋€結(jié)點(diǎn)中存放后一個節(jié)點(diǎn)中的聲明狀態(tài),因此
//如果頭結(jié)點(diǎn)的waitStatus不為0,就說明后一個結(jié)點(diǎn)是一個可被喚醒的狀態(tài)
if (h != null && h.waitStatus != 0)
//喚醒
unparkSuccessor(h);
return true;
}
return false;
}
//嘗試去釋放鎖
protected final boolean tryRelease(int releases) {
//修改同步狀態(tài)器state
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//將同步狀態(tài)器state 變?yōu)?0,說明當(dāng)前同步狀態(tài)器可以進(jìn)行鎖的獲取了
if (c == 0) {
free = true;
//置空當(dāng)前線程
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
最后在這個unparkSuccessor方法中,也有這個具體的unpark喚醒操作
if (s != null)
LockSupport.unpark(s.thread)
通過上述代碼描述,也驗(yàn)證了一開始的猜想:自旋,加鎖,LockSupport,隊(duì)列(LinkQueue)
?
原文鏈接:https://blog.csdn.net/zhenghuishengq/article/details/125648495
相關(guān)推薦
- 2022-04-05 svn使用命令忽略指定目錄 svn propset svn:ignore “要忽略的目錄“ .
- 2022-03-22 詳解jQuery的核心函數(shù)和事件處理_jquery
- 2022-03-17 golang實(shí)現(xiàn)數(shù)組分割的示例代碼_Golang
- 2022-05-31 Python?Pyecharts繪制箱線圖詳解_python
- 2022-05-03 python單例模式之selenium?driver實(shí)現(xiàn)單例_python
- 2022-10-17 詳解Go語言中單鏈表的使用_Golang
- 2023-03-26 python命令行參數(shù)argparse模塊基本用法詳解_python
- 2022-09-15 Linux添加Python?path方法及修改環(huán)境變量的三種方法_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支