並發是個什麼鬼之同步工具類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();              }          }      }    }

Tags: