countdownlatch簡介
countdownlatch是基於aqs實現的乙個執行緒同步工具,也稱為閉鎖。它的作用是讓乙個或者多個執行緒等待某個事件的發生。簡單的理解為countdownlatch有乙個正數計數器,countdown方法對計數器做減操作,await方法等待計數器達到0。所有await的執行緒都會阻塞直到計數器為0或者等待執行緒中斷或者超時。
countdownlatch唯一的構造方法countdownlatch(int count),當在閉鎖上呼叫countdown()方法時,閉鎖的計數器將減1,當閉鎖計數器為0時,閉鎖將開啟,所有等待的執行緒將通過閉鎖開始執行。
下面例子是讓兩個執行緒等待另外兩個執行緒。
public
class
work
implements
runnable
public
void
run(
)catch
(interruptedexception e)
system.out.
println
(this
.name +
" done!");
this
.downlatch.
countdown()
;}public
static
void
main
(string[
] args)
throws interruptedexception
catch
(interruptedexception e)
system.out.
println
("waiting over!");
}).start()
; latch.
await()
; system.out.
println
("all done");
}}
實現原理
下面分析countdownlatch的實現原理,先從它的構造方法入手。
public
countdownlatch
(int count)
sync繼承了aqs,aqs中的state在這裡表示計數值得大小,即等待幾個執行緒的意思。
private
static
final
class
sync
extends
abstractqueuedsynchronizer
intgetcount()
}
await
在前面的例子中,我們知道了 await 的作用是等待計數器減為0,否則一直阻塞,直到超時或者中斷。這裡我們分析無參的方法。
public
void
await()
throws interruptedexception
// aqs
public
final
void
acquiresharedinterruptibly
(int arg)
throws interruptedexception
protected
inttryacquireshared
(int acquires)
如果執行緒已經中斷,則丟擲異常,否則,去獲取鎖,也就是判斷state(計數器)是否為0。如果不為0,那麼進入doacquiresharedinterruptibly方法去掛起執行緒。
private
void
doacquiresharedinterruptibly
(int arg)
throws interruptedexception }if
(shouldparkafte***iledacquire
(p, node)
&&parkandcheckinterrupt()
)throw
newinterruptedexception()
;}}finally
}
這段**和我們在aqs中分析acquirequeued方法邏輯基本是一樣的,多了一步建立node節點,並且node是定義成共享的node.shared,並且加入到同步佇列中。被喚醒後重新嘗試獲取鎖,不只設定自己為head,還需要通知其他等待的執行緒。嘗試去獲取鎖,如果返回值大於0,表示獲取成功,則去喚醒後面的節點。
private
void
setheadandpropagate
(node node,
int propagate)
}
sethead設定頭節點後,取出下乙個節點,如果也是共享型別,進行doreleaseshared釋放操作。下個節點被喚醒後,重複上面的步驟,達到共享狀態向後傳播。
countdown
countdown是釋放鎖的過程,releaseshared在aqs中實現,tryreleaseshared是由countdownlatch內部類sync實現。
public
void
countdown()
public
final
boolean
releaseshared
(int arg)
return
false
;}
嘗試釋放鎖,如果完全釋放通知喚醒佇列中的執行緒,完全釋放指的是state減為0,同步佇列中儲存的是呼叫了await方法的執行緒。
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;}
}
如果頭結點的狀態為signal,通過cas置位0,並呼叫unparksuccessor喚醒下個節點。被喚醒的節點狀態會重置成0,在下一次迴圈中被設定成propagate狀態,代表狀態要向後傳播。此方法的出口只有乙個,即 h == head條件,當同步佇列中沒有可以喚醒的節點時,此條件成立。
private
void
unparksuccessor
(node node)
if(s != null)
locksupport.
unpark
(s.thread)
;}
這裡需要注意在aqs中,我們分析到head節點是持有鎖的執行緒,在此處,head節點是沒有執行緒與它對應,head節點後面的都是阻塞執行緒。unparksuccessor方法的作用是尋找下乙個可用節點(status<0),將它的狀態置位0,並且喚醒。 Java併發程式設計系列 CyclicBarrier
cyclicbarrier簡介 cyclicbarrier也是基於reentrantlock和condition的乙個同步工具類,它的作用是讓一些執行緒到達某個公共屏障點時,等待未到達的執行緒。當所有執行緒到達屏障點時,繼續往下執行。先看乙個例子 public class cyclicbarrier...
Java的高併發程式設計系列(三)
鎖定某物件o,如果o的屬性發生改變,不影響鎖的使用,但是如果o變成另外乙個物件,則鎖定的物件發生改變,應該避免將鎖定物件的引用變成另外乙個物件。public class demo17 catch interruptedexception e system.out.println thread.cur...
JAVA併發程式設計
通過常量字串 string 來呼叫 wait 或 notify 方法所導致的問題是,jvm 編譯器會在內部自動將內容相同的 string 轉變為相同的物件。這意味著,即便你建立了兩個不同的 mywaitnotify 例項,他們內部的 mymonitorobject 變數也會指向相同的 string ...