並發是個什麼鬼之同步工具類CountDownLatch
- 2019 年 12 月 5 日
- 筆記
CountDownLatch 又是大神 Doug Lea的又一神作,正如每個Java文檔所描述的那樣,CountDownLatch是一個同步工具類,它允許一個或多個執行緒一直等待,直到其他執行緒的操作執行完後再執行。
CountDownLatch是通過一個計數器來實現的,計數器的初始值為執行緒的數量。每當一個執行緒完成了自己的任務後,計數器的值就會減1。當計數器值到達0時,它表示所有的執行緒已經完成了任務,然後在閉鎖上等待的執行緒就可以恢復執行任務。

源碼分析
源碼位於rt.java中的java.util.concurrent包中。
1、CountDownLatch:A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
中文解釋:也就是說主執行緒在等待所有其它的子執行緒完成後再往下執行
2、構造函數:CountDownLatch(int count)//初始化count數目的同步計數器,只有當同步計數器為0,主執行緒才會向下執行
主要方法:void await()//當前執行緒等待計數器為0 boolean await(long timeout, TimeUnit unit)//與上面的方法不同,它加了一個時間限制。 void countDown()//計數器減1 long getCount()//獲取計數器的值
3.它的內部有一個輔助的內部類:sync. 它的實現如下:
/** * 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; } } }4.await()方法的實現
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); //調用3中的tryAcquireShared()方法 if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg);//加入到等待隊列中 }5.countDown()方法的實現
public void countDown() { sync.releaseShared(1); } public final boolean releaseShared(int arg) { //調用3中的tryReleaseShared()方法 if (tryReleaseShared(arg)) { //解鎖 doReleaseShared(); return true; } return false; }應用場景
比如主任務是一個比較複雜的運算,為了節約時間,我們可以拆分成多個子任務,多執行緒同時執行,最終統一匯總任務。
其實生活也有類似的場景,比如馬拉松賽跑,我們不可能按順序依次跑步,這樣得跑到猴年馬月。一般來說都是大家聽到發號命令一起跑,最終比賽結束,統一匯總成績。
程式碼案例
下面看一個例子大家就清楚CountDownLatch的用法了:
/** * CountDownLatch,一個同步輔助類,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待 * 構造方法參數指定了計數的次數 * countDown方法,當前執行緒結束執行後調用,計數減一 * awaint方法,調用此方法會一直阻塞當前執行緒,直到計時器的值為0 * 創建者 科幫網 * 創建時間 2017年8月15日 * */ public class CountDownLatchDemo { final static SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); final static String startTime = sdf.format(new Date()); public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(2);// 兩個賽跑者 Runer runer1 = new Runer("劉翔", 5000, latch); Runer runer2 = new Runer("羅伯斯", 8000, latch); runer1.start();//劉翔 開始跑步 runer2.start();//羅伯斯 開始跑步 latch.await();// 等待所有人賽跑結束 System.out.println("all runer done at " + sdf.format(new Date())); } static class Runer extends Thread { String runerName; int runTime; CountDownLatch latch; public Runer(String runerName, int runTime, CountDownLatch latch) { this.runerName = runerName; this.runTime = runTime; this.latch = latch; } public void run() { System.out.println("Runer " + runerName + " do run begin at " + startTime); doRun();//跑步 System.out.println("Runer " + runerName + " do run complete at " + sdf.format(new Date())); latch.countDown();// 終點結束,計數器減一 } private void doRun() { try { Thread.sleep(runTime); } catch (InterruptedException e) { e.printStackTrace(); } } } }

