在過去咱們實現多線程同步的代碼中,每每使用join()、wait()、notiyAll()等線程間通訊的方式,隨着JUC包的不斷的完善,java爲咱們提供了豐富同步工具類,官方也鼓勵咱們使用工具類來實現多線程的同步,今天咱們就對其中CountDownLatch類的使用與底層實現進行分析與總結。
1、CountDownLatch使用
CountDownLatch其實能夠看作一個計數器,統計多個線程執行完成的狀況,適用於控制一個或多個線程等待,直到全部線程都執行完畢的場景,相似與Thread.join()的做用。下面咱們經過一個簡單的例子看下CountDownLatch的使用。
複製代碼
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(5);
// 啓動計數線程
for (int i = 0; i < 5; i++) {
new CountDownLatchThread(i, countDownLatch).start();
}
// 啓動等待線程
for (int i = 0; i < 5; i++) {
new Thread() {
public void run() {
try {
countDownLatch.await(www.bdqxylgw.com );
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("計數完畢了," + Thread.currentThread().getName() + "等待線程執行");
}
}.start();
}
}
複製代碼
計數線程代碼:
複製代碼
public class CountDownLatchThread extends Thread {
private CountDownLatch www.rmutk.net countDownLatch;
private int name;
private int count;
public CountDownLatchThread(int name, CountDownLatch countDownLatch) {
this.name = name;
this.countDownLatch =www.chaoyuuyule.com countDownLatch;
this.count = 0;
}
public void run() {
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(100);
count++;
}
System.out.println(name + "號線程--" + Thread.currentThread().getName() + "--計數完成了");
countDownLatch.countDown(www.yaoshiyulegw.com);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace(www.yifayuled.cn);
}
}
}
複製代碼
輸出結果:
複製代碼
1號線程--Thread-1--計數完成了
0號線程--Thread-0--計數完成了
4號線程--Thread-4--計數完成了
2號線程--Thread-2--計數完成了
3號線程--Thread-3--計數完成了
計數完畢了,Thread-5等待線程執行
計數完畢了,Thread-6等待線程執行
計數完畢了,Thread-7等待線程執行
計數完畢了,Thread-8等待線程執行
計數完畢了,Thread-9等待線程執行
複製代碼
經過上面的例子能夠看到,利用CountDownLatch的countDown方法與await()方法,咱們能夠同步計數線程與等待線程,使等待線程在全部計數線程完成後再開始運行。
2、CountDownLatch源碼分析
接下來咱們對countDownLatch內部源碼進行一下分析。
一、CountDownLatch的構造。
首先看下CountDownLatch的構造函數
public CountDownLatch(int count) {
if (count <www.xcdeyiju.com 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
CountDownLatch的構造函數會接收一個count值作爲計數器,也就是若是你須要等待N個線程執行結束,那這裏就傳入N。同時CountDownLatch會實例化一個Sync對象,這個Sync實際上是CountDownLatch內部定義的一個繼承自AbstractQueuedSynchronizer的實現類,因此CountDownLatch提供的同步和其餘功能都是圍繞Sync這個子類實現的,也就是基於AbstractQueuedSynchronizer類來實現的。
咱們來看下Sync這個類的定義
複製代碼
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
/**
一、設置AbstractQueuedSynchronizer中同步狀態的值state,也就是計數器的值。
二、這個值volatile變量,必須保證線程間的可見性;
**/
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
//獲取同步狀態的值
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// 經過CAS操做改變同步狀態值,保證同步狀態的值state的線程安全。
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
複製代碼
二、countDown方法
首先咱們看下countDown方法的源碼
public void countDown() {
//改變同步狀態值,線程執行完成時計數器減一
sync.releaseShared(1);
}
AbstractQueuedSynchronizer類中releaseShared() 方法的源碼
複製代碼
public final boolean releaseShared(int arg) {
// CountDownLatch定義的子類Sync實現,經過CAS操做改變State的值
if (tryReleaseShared(arg)) {
//State以遞減爲0,表明着全部執行線程執行完畢,共享模式下釋放鎖,那麼等待線程就可以拿到鎖往下執行。
doReleaseShared();
return true;
}
return false;
}
複製代碼
當調用CountDownLatch的countDown方法時,就會執行計數器進行減一操做,直到全部線程所有執行完畢,計算器爲0時喚醒等待線程。
AbstractQueuedSynchronizer中doReleaseShared方法是執行共享模式下釋放鎖的操做,從而讓等待線程獲取鎖,繼續向下執行。
三、await方法
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
AbstractQueuedSynchronizer類中acquireSharedInterruptibly() 方法的源碼
複製代碼
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//獲取同步狀態值
if (tryAcquireShared(arg) < 0)
//同步狀態值即計數器的值不爲0,等待線程共享模式下嘗試獲取鎖,獲取不到鎖的話進入阻塞
doAcquireSharedInterruptibly(arg);
}
複製代碼
await方法的實現也很明確,首頁獲取同步狀態也就是計數器的值,若是爲0即全部線程執行完畢返回1,不然返回-1的話,等待線程在共享模式下嘗試獲取鎖,獲取不到鎖的話進入阻塞;
AbstractQueuedSynchronizer中doAcquireSharedInterruptibly方法是執行共享模式下獲取鎖的操做;
3、總結
經過上面分析能夠看到CountDownLatch是基於AbstractQueuedSynchronizer類實現的,一個很是實用的多線程控制工具類,它相似與一個計數器用來控制指定的線程等待,直到計數器歸零。以上咱們對CountDownLatch類的使用與核心方法的源碼進入了必定的分析,其中若有不足與不正確的地方還望指出與海涵。java