玩轉併發 深入AQS和CountDownLatch

2021-09-11 02:40:50 字數 4522 閱讀 9394

countdownlatch中count down是倒數的意思,latch則是門閂的含義。整體含義可以理解為倒數的門栓,似乎有一點「三二一,芝麻開門」的感覺。countdownlatch的作用也是如此,在構造countdownlatch的時候需要傳入乙個整數n,在這個整數「倒數」到0之前,主線程需要等待在門口,而這個「倒數」過程則是由各個執行執行緒驅動的,每個執行緒執行完乙個任務「倒數」一次。總結來說,countdownlatch的作用就是等待其他的執行緒都執行完任務,必要時可以對各個任務的執行結果進行彙總,然後主線程才繼續往下執行。

countdownlatch主要有兩個方法:countdown()和await()。countdown()方法用於使計數器減一,其一般是執行任務的執行緒呼叫,await()方法則使呼叫該方法的執行緒處於等待狀態,其一般是主線程呼叫。這裡需要注意的是,countdown()方法並沒有規定乙個執行緒只能呼叫一次,當同乙個執行緒呼叫多次countdown()方法時,每次都會使計數器減一;另外,await()方法也並沒有規定只能有乙個執行緒執行該方法,如果多個執行緒同時執行await()方法,那麼這幾個執行緒都將處於等待狀態,並且以共享模式享有同乙個鎖

public

static

void

main

(string[

] args)

catch

(interruptedexception e)}}

; thread thread2 =

newthread()

catch

(interruptedexception e)}}

; thread1.

start()

; thread2.

start()

;}

列印結果:

釋放latch

do other thing

countdownlatch的資料結構

從原始碼可知,其底層是由aqs提供支援,所以其資料結構可以參考aqs的資料結構,而aqs的資料結構核心就是兩個虛擬佇列:同步佇列sync queue 和條件佇列condition queue,不同的條件會有不同的條件佇列。它可以用來實現可以依賴 int 狀態的同步器,獲取和釋放引數以及乙個內部fifo等待佇列深入理解aqs原理

countdownlatch原始碼

countdownlatch

(int count)

構造乙個用給定計數初始化的 countdownlatch。

// 使當前執行緒在鎖存器倒計數至零之前一直等待,除非執行緒被中斷。

void

await()

// 使當前執行緒在鎖存器倒計數至零之前一直等待,除非執行緒被中斷或超出了指定的等待時間。

boolean

await

(long timeout, timeunit unit)

// 遞減鎖存器的計數,如果計數到達零,則釋放所有等待的執行緒。

void

countdown()

// 返回當前計數。

long

getcount()

// 返回標識此鎖存器及其狀態的字串。

string tostring

()

建構函式:

//count小於0,則丟擲異常

public

countdownlatch

(int count)

內部類sync,sync繼承於aqs類

private

static

final

class

sync

extends

abstractqueuedsynchronizer

//在aqs中,state是乙個private volatile long型別的物件。

//對於countdownlatch而言,state表示的」鎖計數器「。

//countdownlatch中的getcount()

//最終是呼叫aqs中的getstate(),返回的state物件,即」鎖計數器「。

intgetcount()

//嘗試獲取共享鎖

protected

inttryacquireshared

(int acquires)

//嘗試釋放共享鎖

protected

boolean

tryreleaseshared

(int releases)

}}

//countdownlatch持有aqs鎖的成員變數

private

final sync sync;

await()方法

呼叫 await() 方法時,先去獲取 state 的值,當計數器不為0的時候,說明還有需要等待的執行緒在執行,則呼叫 doacquiresharedinterruptibly 方法,進來執行的第乙個動作就是嘗試加入等待佇列 ,即呼叫 addwaiter()方法

public

void

await()

throws interruptedexception

//aqs中方法

public

final

void

acquiresharedinterruptibly

(int arg)

throws interruptedexception

//sync中嘗試獲取共享鎖

protected

inttryacquireshared

(int acquires)

假設此時共享鎖獲取失敗,則開始呼叫doacquiresharedinterruptibly()方法

將當前執行緒加入等待佇列,並通過 parkandcheckinterrupt()方法實現當前執行緒的阻塞

//aqs中模板方法

private

void

doacquiresharedinterruptibly

(int arg)

throws interruptedexception

}//判斷當前節點是否需要阻塞,如果當前節點的前繼節點不是head,則需要if(

shouldparkafte***iledacquire

(p, node)

&&//阻塞執行緒

parkandcheckinterrupt()

)throw

newinterruptedexception()

;}}finally

}//喚醒節點

private

void

setheadandpropagate

(node node,

int propagate)

}//釋放執行緒,實際就是設定waitstatus為0

private

void

doreleaseshared()

else

if(ws ==0&&

!compareandsetwaitstatus

(h,0

, node.propagate)

)continue

;// loop on failed cas

}//在迴圈過程中,為了防止在上述操作中新增了新節點的情況

//通過檢測頭節點是否改變,如果改變了就繼續迴圈

if(h == head)

// loop if head changed

break;}

}//解除阻塞

private

void

unparksuccessor

(node node)

//呼叫unsafe類的unpark,解除阻塞

//這是cpu級別的操作

if(s != null)

locksupport.

unpark

(s.thread)

;}

呼叫await()方法的執行緒想要獲取鎖,有兩個條件:

countdonw()釋放鎖

當計數器為 0 後,會喚醒等待佇列裡的所有執行緒,所有呼叫了 await() 方法的執行緒都被喚醒,併發執行

public

void

countdown()

//sync中方法

public

final

boolean

releaseshared

(int arg)

return

false;}

//sync中方法

protected

boolean

tryreleaseshared

(int releases)

}

使用countdown,每次使用計數器值-1,當計數器值為0,釋放執行緒。

總結

深入鎖和併發的核心 併發工具類(3)

這三個類都是在定義在jdk的並發包util.concurrent中的併發工具類,用來實現多種不同用途的同步元件。我更願意將其稱為柵欄,有了circle是說明這個工具是可以迴圈使用的。當乙個業務的情景適用於要等到多個執行緒同時準備好後,比如各個分銀行的業務處理完成後,總銀行才能進行結賬這樣的場景。就可...

C語言的入門和深入

您可以使用渲染latex數學表示式 katex gamma公式展示 n n 1 n n gamma n n 1 quad forall n in mathbb n n n 1 n n 是通過尤拉積分 z 0 tz 1e t dt gamma z int 0 infty t e dt z 0 t z ...

深入玩轉K8S之簡單的業務彈性伸縮和滾動更新操作

首先是彈性伸縮,很簡單就是通過編寫deployment檔案,把副本數增大,就完成了業務的彈性擴充套件,那麼擴充套件完了怎麼調小呢,那麼也按照剛才的方法進行調整副本大小即可。最後說下滾動更新的操作,也很簡單跟剛才彈性伸縮差不多,為了方便區分,這裡弄了多個deployment,按照v1,v2來區分。ok...