同步計數器 CountDownLatch

CountDownLatch 是一個同步工具類,它容許一個或多個線程一直等待,直到其餘線程的操做執行完後再執行.html

CountDownLatch 是經過一個計數器來實現的,計數器的初始值爲線程的數量。每當一個線程完成了本身的任務後,計數器的值就會減1。當計數器值到達0時,它表示全部的線程已經完成了任務,而後在閉鎖上等待的線程就能夠恢復執行任務。java

 

主要方法有:api

  • CountDownLatch (int count),構造一個用給定計數器初始化的CountDownLatch。構造器中的計數值(count)實際上就是閉鎖須要等待的線程數量。這個值只能被設置一次,並且CountDownLatch沒有提供任何機制去從新設置這個計數值
  • void await (),使當前線程在鎖存器倒計數至0以前一直等待,除非線程被中斷。
  • boolean await (long timeout, TimeUnit unit),使當前線程在鎖存器,倒計數至0以前一直等待,除非線程被中斷或超出了指定的等待時間。
  • void countDwon (),遞減鎖存器的計數,若是計數達到0,則釋放全部等待的線程。
  • long getCount (),返回當前計數。

使用場景:多線程

在一些應用場景中,須要等待某個條件達到要求後才能作後面的事情;同時當線程都完成後也會觸發事件,以便進行後面操做。oracle

CountDownLatch的countDown() 和 await(),前者主要倒數一次,後者是等待倒數到0,若是沒有到0,就只有阻塞等待了。ide

  • 應用場景1:開5個多線程去下載,當5個線程都執行完了,纔算下載成功。
  • 應用場景2:當用戶多文件上傳時,能夠採用多線程上傳,當多個文件都上傳成功時,纔算真正的上傳成功。
  • 實現最大的並行性:有時咱們想同時啓動多個線程,實現最大程度的並行性。例如,咱們想測試一個單例類。若是咱們建立一個初始計數爲1的CountDownLatch,並讓全部線程都在這個鎖上等待,那麼咱們能夠很輕鬆地完成測試。咱們只需調用 一次countDown()方法就可讓全部的等待線程同時恢復執行。
  • 開始執行前等待n個線程完成各自任務:例如應用程序啓動類要確保在處理用戶請求前,全部N個外部系統已經啓動和運行了。
  • 死鎖檢測:一個很是方便的使用場景是,你可使用n個線程訪問共享資源,在每次測試階段的線程數目是不一樣的,並嘗試產生死鎖。
 1 public class Demo {
 2 
 3     public static void main(String[] args) throws Exception {
 4 
 5         CountDownLatch latch = new CountDownLatch(3);
 6         Worker worker1 = new Worker("xiao ming", latch);
 7         Worker worker2 = new Worker("xiao hong", latch);
 8         Worker worker3 = new Worker("xiao wang", latch);
 9 
10         worker1.start();
11         worker2.start();
12         worker3.start();
13         
14         latch.await();
15         
16         System.out.println("Main Thread End.");
17     }
18 
19     static class Worker extends Thread {
20 
21         private String workerName;
22         private CountDownLatch latch;
23 
24         public Worker(String workerName, CountDownLatch latch) {
25 
26             this.workerName = workerName;
27             this.latch = latch;
28         }
29 
30         @Override
31         public void run() {
32 
33             try {
34                 System.out.println("Worker:" + workerName + " is begin.");
35                 Thread.sleep(1000L);
36                 System.out.println("Worker:" + workerName + " is end.");
37             } catch (Exception e) {
38                 e.printStackTrace();
39             }
40             latch.countDown();
41         }
42     }
43 }

輸出:工具

1 Worker:xiao ming is begin.
2 Worker:xiao hong is begin.
3 Worker:xiao wang is begin.
4 Worker:xiao ming is end.
5 Worker:xiao wang is end.
6 Worker:xiao hong is end.
7 Main Thread End.

CountDownLatch 的實現原理:測試

CountDownLatch 的核心實現機制利用 AbstractQueuedSynchronizer 簡稱「AQS」的 state狀態來實現Count的阻塞機制。ui

 1 public class CountDownLattch {
 2 
 3     /**
 4      *CountDownLatch 的核心實現機制利用 AbstractQueuedSynchronizer 簡稱「AQS」的 state狀態來實現Count的阻塞機制
 5      */
 6 
 7     private static final class Sync extends AbstractQueuedSynchronizer {
 8 
 9         Sync(int count) {
10             setState(count);
11         }
12 
13         int getCount() {
14             return getState();
15         }
16 
17         protected int tryAcquireShared(int acquires) {
18             return (getState() == 0) ? 1 : -1;
19         }
20 
21         protected boolean tryReleaseShared(int releases) {
22             // 覆蓋"AQS"的釋放狀態方式,實現本身的邏輯,來消減count的線程數
23             for(;;) {
24 
25                 int c = getState();
26                 if (c == 0)
27                     return false;
28 
29                 int nextc = c - 1;
30                 if (compareAndSetState(c, nextc))
31                     return nextc == 0;
32             }
33         }
34 
35     }
36 
37     private final Sync sync;
38     // 利用"AQS"的state狀態,來標示線程的count的數量
39 
40     public CountDownLat(int count) {
41         this.sync = new Sync(count);
42     }
43 
44     // 利用"AQS"得到一個共享模式下的完成狀態
45     public void await() throws InterruptedException {
46         sync.acquireSharedInterruptibly(1);
47     }
48 
49     // 利用"AQS"得到一個共享模式下的完成狀態,超出了指定的等待時間
50     public void await(int timeout, TimeUnit unit) throws InterruptedException {
51         sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
52     }
53 
54     // 利用"AQS"在共享模式下釋放狀態也就是數字減一
55     public void countDown() {
56         sync.releaseShared(1);
57     }
58 
59     // 調用"AQS"返回當前計數
60     public long getCount() {
61         return sync.getCount();
62     }
63 
64     ...
65 }
相關文章
相關標籤/搜索