Java多線程同步工具類之CountDownLatch

  在過去咱們實現多線程同步的代碼中,每每使用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

相關文章
相關標籤/搜索