CountDownLatch源碼的理解
設計理念
CountDownLatch對線程的阻塞、釋放是基於隊列+計數器來實現的。
簡單的例子說明
現在有如下代碼:
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){
public void run() {
try {
System.out.println("線程1執行");
Thread.sleep(5000);
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
System.out.println("線程2執行");
Thread.sleep(3000);
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
System.out.println("線程3阻塞");
latch.await();
System.out.println("線程3繼續執行");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
try {
Thread.sleep(1000);
System.out.println("主線程線程阻塞");
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主線程繼續執行");
}
運行後的效果
線程1執行
線程2執行
線程3阻塞
主線程線程阻塞
線程3繼續執行
主線程繼續執行
結果說明:當latch中的計數器變爲0時,調用await()的線程會繼續執行,繼續執行的順序跟調用await()的先後有關。
隊列+計數器的說明
計數器的簡單說明
爲了簡化流程圖,假設只有一個子線程調用了countDown()方法,只有主線程調用到了await()方法
上面的流程圖只是說明計數器對各個線程的影響,個人不才,實在是畫不出多線程+隊列的的流程圖。
隊列的簡單說明
每個線程調用await(),會向隊列中添加一個node,拿剛纔的代碼說明:
1. 線程3調用await(),會在head和麪添加一個node1(Head是自動生成的)
- 主線程線程調用await(),會在已有的隊列中添加一個node2
- 當計數器爲0,各個線程被喚醒的時候,會判斷線程對應node的前一個節點是否是Head,如果是Head,將當前的node設置爲Head,再次喚醒線程。如果不是Head,繼續掛起。對於上述代碼:
- 假如喚醒線程的時候,主線程首先執行:
- 假如喚醒線程的時候,線程3首先執行:
- 假如喚醒線程的時候,主線程首先執行:
以上這些流程圖只是爲了將單個功能更形象的表現出來,如果要完全理解,還是要回到源碼中看邏輯
代碼註釋
CountDownLatch 裏面的代碼
public class CountDownLatch {
//AbstractQueuedSynchronizer的具體類,真正控制線程的操作在這裏實現
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//構造方法,設置state,這裏理解爲設置計數器的大小
Sync(int count) {
setState(count);
}
//獲取state,這裏理解爲獲取計數器的大小
int getCount() {
return getState();
}
//嘗試獲取所,如果計數器的大小爲0,返回1;
//大小不爲0,返回-1
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)
//如果鎖已經全部釋放了,返回false
return false;
int nextc = c-1;
//通過CAS,更新計數器的大小
if (compareAndSetState(c, nextc))
//釋放一個鎖(也就是計數器-1)以後,如果再也沒有鎖(計數器爲0),返回true,如果還有鎖(計數器不爲0),返回false
return nextc == 0;
}
}
}
private final Sync sync;
//構造方法,計數器實際上是AbstractQueuedSynchronizer具體類的state
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
//阻塞線程,等待計數器爲0,
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//阻塞線程,等待計數器爲0,或者超過給定的時間
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//計數器的大小減去1
public void countDown() {
sync.releaseShared(1);
}
//獲取計數器的大小
public long getCount() {
return sync.getCount();
}
//不解釋
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
AbstractQueuedSynchronizer中有關await()中的部分代碼
//await()中實際上是調用該方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//處理異常
if (Thread.interrupted())
throw new InterruptedException();
//嘗試獲取鎖,在這裏,是調用CountDownLatch當中AbstractQueuedSynchronizer具體類中重寫的方法
//計數器大小爲0,返回1;否則爲-1;
if (tryAcquireShared(arg) < 0)
//計數器大小不爲0,執行該方法
doAcquireSharedInterruptibly(arg);
}
//計數器不爲0時,調用該方法
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//向隊列中添加一個節點,(如果當前隊列沒有head,先創建head,再添加node)
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
//開啓自旋,如果計數器爲0,會結束自旋
for (;;) {
//獲取當前node的前一個node
final Node p = node.predecessor();
//判斷當前node是不是第一個node
if (p == head) {
//如果是第一個node,查看計數器是否爲0
int r = tryAcquireShared(arg);
if (r >= 0) {
//計數器爲0,說明所有的鎖都釋放了
//將當前的node設置爲head,也就是將隊列中第一個node設置爲head,同時也會喚醒掛起的線程
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
//該方執行完畢,調用await()的線程可以繼續執行
return;
}
}
//將調用await()的線程掛起
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
AbstractQueuedSynchronizer中有關countDown()中的部分代碼
//countDown()實際上調用的是該方法
public final boolean releaseShared(int arg) {
//嘗試釋放鎖,在這裏,是調用CountDownLatch當中AbstractQueuedSynchronizer具體類中重寫的方法
//計數器-1,返回true,計數器大小沒變,返回false
if (tryReleaseShared(arg)) {
//計數器大小-1,進入判斷內部
doReleaseShared();
return true;
}
return false;
}
//計數器-1後,調用該方法
private void doReleaseShared() {
//開啓自旋
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
//使用continue,是爲了確保Status的狀態一定會修改成功
continue; // loop to recheck cases
//喚醒掛起的線程,也就是doAcquireSharedInterruptibly()當中的自旋執行一次,可能再次掛起或者結束
unparkSuccessor(h);
}
//喚醒線程後,head值改變的話,更新Status爲PROPAGATE
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
//確保Status一定會修改成功
continue; // loop on failed CAS
}
//判讀head是否更換(喚醒掛起的線程,有可能將head更新,也可能不更新)
if (h == head) // loop if head changed
//在這裏,可以粗略的理解爲:如果喚醒線程後,該線程仍然掛起,結束該方法的自旋
break;
//如果head改變,繼續自旋,這樣,就會調用compareAndSetWaitStatus(h, 0, Node.PROPAGATE)
}
}
結語
上文中沒有分析await(long timeout, TimeUnit unit),留給各位Monkey去分析,順便練習下看源碼的能力。
看了這麼多的分析,不要忘了CountDownLatch的作用:
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
簡答說明:等待相應的線程結束後再去執行相關的代碼。
源碼作者:Doug Lea
轉載請標明出處:http://blog.csdn.net/qq_26411333/article/details/51416373