AQS原始碼解析 上

2021-10-05 08:28:09 字數 3433 閱讀 6131

clh佇列:三個人發明的,三個人各取乙個字母命名。

aqs(abstractqueuedsynchronizer)裡面有基本的模板方法,包括操作同步佇列和條件佇列等。

繼承aqs子類可以自己選擇實現的方法有:

原始碼解析中術語說明

//獲取獨佔鎖

public

final

void

acquire

(int arg)

private node addwaiter

(node mode)

}enq

(node)

;//如果尾節點為空的話進入enq邏輯

return node;

}

private node enq

(final node node)

else}}

}

//進入同步隊後的邏輯,比如檢測和休眠操作

final

boolean

acquirequeued

(final node node,

int arg)if(

shouldparkafte***iledacquire

(p, node)

&&//檢測pre的狀態以及相關操作

parkandcheckinterrupt()

)//if pre==signal,then park

interrupted =

true;}

}finally

}

private

static

boolean

shouldparkafte***iledacquire

(node pred, node node)

while

(pred.waitstatus >0)

; pred.next = node;

//直至找到乙個有效狀態的,此時才把pred.next = node,

//所以這中間有很大的時間差,這也就是為什麼後續為了找到乙個可喚醒的節點採用倒序的原因

}else

return

false

;}

private

final

boolean

parkandcheckinterrupt()

//釋放鎖操作

public

final

boolean

release

(int arg)

return

false

;}

private

void

unparksuccessor

(node node)

if(s != null)

//喚醒指定的執行緒

locksupport.

unpark

(s.thread)

;}

//獲取共享鎖

public

final

void

acquireshared

(int arg)

//進入同步佇列的邏輯和獨佔鎖的邏輯大致相同,唯有的不同就是:

//在獲取到共享鎖的時候,會傳遞喚醒其後面的所有共享節點直至遇到乙個獨佔節點;

//當前上面的說法是在資源充裕的情況下

//至於能喚醒後面幾個節點還取決於剩餘的資源量,即下面**的r變數的值

private

void

doacquireshared

(int arg)}if

(shouldparkafte***iledacquire

(p, node)

&&parkandcheckinterrupt()

) interrupted =

true;}

}finally

}

//設定head,最重要的是傳遞共享狀態到後續節點

private

void

setheadandpropagate

(node node,

int propagate)

}

//釋放共享鎖

public

final

boolean

releaseshared

(int arg)

return

false

;}

private

void

doreleaseshared()

else

if(ws ==0&&

//我們稱這個操作為「0-3」

!compareandsetwaitstatus

(h,0

, node.propagate)

)continue

;// loop on failed cas

}//1.本次迴圈結束的時候head還沒用發生變化,為了保證sethead的有序性,所以退出迴圈。

沒發生變化,說明沒用符合條件的需要喚醒的節點

if(h == head)

break;}

}

首先明白一點,同一時間可能有多個執行緒都在執行doreleaseshared,也就是都在嘗試去喚醒first node,但是喚醒之前需要更改head status,由於這一步是cas操作,所以只會有乙個執行緒成功並unpark,並且這個被喚醒的執行緒,在經過一些邏輯判斷後,會進一步傳遞這個share的狀態。

更改head的操作,肯定是乙個已經獲取到資源的執行緒,並且sethead操作是乙個普通的非cas操作,所以同時就有可能很多執行緒都在sethead,那麼怎麼保證sethead是按順序的呢,也就是按順序喚醒?

我們知道doreleaseshared裡面有個迴圈操作,當一次迴圈結束的時候,會檢測head是否發生變化,如果發生了變化,說明sethead已經完畢,執行緒喚醒並且獲得資源完畢,可以進行下次的迴圈繼續喚醒,否則的話,直接退出迴圈。

上面的doreleaseshared的邏輯可能的執**況如下:

head status -1

head status 0

個人理解:

在短時間內會有大量的執行緒被喚醒,但是這些被喚醒的執行緒不一定都能獲得資源,如果獲取不到資源的話會繼續park;所謂的傳播propagate,不是乙個執行緒把狀態轉播到同步佇列的其他節點,而是在不同執行緒間的傳播,乙個執行緒沒幹完的事兒,另外乙個執行緒繼續幹,繼續發揚光大,即傳播。

AQS原始碼解析

公平鎖 fairsync 核心方法 public final void acquire int arg tryacquire arg 方法 protected final boolean tryacquire int acquires 重入鎖 state 1 else if current gete...

AQS原始碼解析

加鎖從 先看一下 acquire方法開始 private transient volatile node head 頭節點 獲取鎖的執行緒節點 private transient volatile node tail 尾節點node 內部類 共享 static final node shared n...

AQS原始碼分析

如上 用jmeter模擬30個請求同時下單,結果30個請求都下單成功,產生了超賣問題。下面實現自定義乙個同步器來實現自定義鎖 2021 7 1 自定義aqs實現 public class mylock public void setstate int state public thread getl...