一酗昼、一個例子
private static void countDownLatchTest(List<String> roles){
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i=0;i<10;i++){
CountRunnable countRunnable = new CountRunnable(i+"",i+"",roles,countDownLatch);
new Thread(countRunnable).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public class CountRunnable implements Runnable{
private String id;
private String name;
private List<String> roles;
private CountDownLatch countDownLatch;
private String password;
public CountRunnable(String id, String name, List<String> roles,
CountDownLatch countDownLatch){
this.name = name;
this.id = id;
this.roles = roles;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
roles.add(name);
System.out.println("執(zhí)行了-id:"+id+"name:"+name+"roles:"+roles.size());
try {
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
輸出結(jié)果:
開始:1515333020630
執(zhí)行了-id:0name:0roles:1
執(zhí)行了-id:1name:1roles:2
執(zhí)行了-id:4name:4roles:4
執(zhí)行了-id:8name:8roles:7
執(zhí)行了-id:3name:3roles:9
執(zhí)行了-id:7name:7roles:10
執(zhí)行了-id:9name:9roles:8
執(zhí)行了-id:6name:6roles:6
執(zhí)行了-id:5name:5roles:5
執(zhí)行了-id:2name:2roles:4
[0, 1, 2, 4, 5, 6, 8, 9, 3, 7]
結(jié)束:1515333021639耗時:1009roles size:10
可以看出石挂,上面每個線程執(zhí)行1000ms,總時長1009ms.
二颠通、源碼介紹
/**
- Constructs a {@code CountDownLatch} initialized with the given count.
- @param count the number of times {@link #countDown} must be invoked
before threads can pass through {@link #await}
- @throws IllegalArgumentException if {@code count} is negative
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
這里看清楚一點:構(gòu)造器中要指定大于0的參數(shù)。
然后創(chuàng)建了一個sync對象致讥,這個Sync是一個內(nèi)部類:
/**
Synchronization control For CountDownLatch.
-
Uses AQS state to represent count.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;Sync(int count) {
setState(count);
}int getCount() {
return getState();
}protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
可以看到仅仆,Sync繼承自AQS類的內(nèi)部類,是CountDownLatch的核心實現(xiàn)垢袱。在內(nèi)部類里有幾個方法:
getCount:獲取當(dāng)前等待的線程數(shù)
tryAcquireShared:如果線程數(shù)為0才返回1墓拜,即當(dāng)前沒有等待的線程
tryReleaseShared:重寫AQS方法,實現(xiàn)每被countDown調(diào)用请契,就將標(biāo)志位-1咳榜,直到標(biāo)志位為0。
而CountDownLatch的里的幾個方法:
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void countDown() {
sync.releaseShared(1);
}
public long getCount() {
return sync.getCount();
}
acquireSharedInterruptibly:
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())//判斷是否發(fā)生中斷
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//注意:-1表示獲取到了共享鎖爽锥,1表示沒有獲取共享鎖
doAcquireSharedInterruptibly(arg);
}
繼續(xù)深入doAcquireSharedInterruptibly涌韩,這段我是看不明白了:
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//由于采用的公平鎖,所以要將節(jié)點放到隊列里
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {//本質(zhì)是等待共享鎖的釋放
final Node p = node.predecessor();//獲得節(jié)點的前繼
if (p == head) { //如果前一個節(jié)點等于前繼
int r = tryAcquireShared(arg);//就判斷嘗試獲取鎖
/*
這里要注意一下r的值就2種情況-1和1:
情況1.r為-1,latch沒有調(diào)用countDown(),state是沒有變化的導(dǎo)致state一直大于0或者調(diào)用了countDown(),但是state不等于0氯夷,直接在for循環(huán)中等待
情況2.r為1,證明countDown(),已經(jīng)減到0,當(dāng)前線程還在隊列中臣樱,state已經(jīng)等于0了.接下來就是喚醒隊列中的節(jié)點
*/
if (r >= 0) {
setHeadAndPropagate(node, r);//將當(dāng)前節(jié)點設(shè)置頭結(jié)點。
p.next = null; // help GC 刪除舊的頭結(jié)點
failed = false;
return;
}
}
//當(dāng)前節(jié)點不是頭結(jié)點肠槽,當(dāng)前線程一直等待擎淤,直到獲取到共享鎖奢啥。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
setHeadAndPropagate:
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // 記錄頭結(jié)點
setHead(node);//設(shè)置node為頭結(jié)點
/*
*從上面?zhèn)鬟f過來 propagate = 1秸仙;
*一定會進入下面的判斷
*/
if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;//獲得當(dāng)前節(jié)點的下一個節(jié)點,如果為最后一個節(jié)點或者桩盲,為shared
if (s == null || s.isShared())
doReleaseShared();//釋放共享鎖
}
}
isShared:
final boolean isShared() {
return nextWaiter == SHARED;
}
釋放共享鎖寂纪,通知后面的節(jié)點:
private void doReleaseShared() {
for (;;) {
Node h = head;//獲得頭結(jié)點
if (h != null && h != tail) {
int ws = h.waitStatus;//獲取頭結(jié)點的狀態(tài)默認值為0
if (ws == Node.SIGNAL) {如果等于SIGNAL喚醒狀態(tài)
//將頭結(jié)點的狀態(tài)置成0,并使用Node.SIGNAL(-1)與0比較,continue赌结,h的狀態(tài)設(shè)置為0,不會再進入if (ws == Node.SIGNAL)
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}//判斷ws是否為0捞蛋,并且h的狀態(tài)不等于0,這里是個坑啊柬姚,ws等于0拟杉,h就是0啊,所以if進不來的,并設(shè)置節(jié)點為PROPAGATE
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
如果頭結(jié)點等于h,其實也沒有一直loop量承,由于上面寫的Node h = head,就算前面的條件都不滿足搬设,這里一定會break
if (h == head) // loop if head changed
break;
}
}
深入到這里穴店,以超出我的想象力。
三拿穴、CountDownLatch總結(jié)
CountDownLatch是通過“共享鎖”實現(xiàn)的泣洞。在創(chuàng)建CountDownLatch中時,會傳遞一個int類型參數(shù)count默色,該參數(shù)是“鎖計數(shù)器”的初始狀態(tài)球凰,表示該“共享鎖”最多能被count個線程同時獲取。當(dāng)某線程調(diào)用該CountDownLatch對象的await()方法時腿宰,該線程會等待“共享鎖”可用時呕诉,才能獲取“共享鎖”進而繼續(xù)運行。而“共享鎖”可用的條件酗失,就是“鎖計數(shù)器”的值為0义钉!而“鎖計數(shù)器”的初始值為count,每當(dāng)一個線程調(diào)用該CountDownLatch對象的countDown()方法時规肴,才將“鎖計數(shù)器”-1捶闸;通過這種方式,必須有count個線程調(diào)用countDown()之后拖刃,“鎖計數(shù)器”才為0删壮,而前面提到的等待線程才能繼續(xù)運行。