瘋狂創客圈 經典圖書 : 《Netty Zookeeper Redis 高併發實戰》 面試必備 + 面試必備 + 面試必備 【博客園總入口 】html
瘋狂創客圈 經典圖書 : 《SpringCloud、Nginx高併發核心編程》 大廠必備 + 大廠必備 + 大廠必備 【博客園總入口 】前端
入大廠+漲工資必備: 高併發【 億級流量IM實戰】 實戰系列 【 SpringCloud Nginx秒殺】 實戰系列 【博客園總入口 】java
Java的concurrent包裏面的CountDownLatch其實能夠把它看做一個計數器,只不過這個計數器的操做是原子操做,同時只能有一個線程去操做這個計數器,也就是同時只能有一個線程去減這個計數器裏面的值。面試
你能夠向CountDownLatch對象設置一個初始的數字做爲計數值,任何調用這個對象上的await()方法都會阻塞,直到這個計數器的計數值被其餘的線程減爲0爲止。編程
CountDownLatch的一個很是典型的應用場景是:有一個任務想要往下執行,但必需要等到其餘的任務執行完畢後才能夠繼續往下執行。假如咱們這個想要繼續往下執行的任務調用一個CountDownLatch對象的await()方法,其餘的任務執行完本身的任務後調用同一個CountDownLatch對象上的countDown()方法,這個調用await()方法的任務將一直阻塞等待,直到這個CountDownLatch對象的計數值減到0爲止。服務器
好比:客戶端一次請求5個統計數據,服務器須要所有統計完成後,才返回客戶端,可使用CountDownLatch 。多線程
//參數count爲計數值 public CountDownLatch(int count) { };
//調用await()方法的線程會被掛起,它會等待直到count值爲0才繼續執行 public void await() throws InterruptedException { }; //和await()相似,只不過等待必定的時間後count值還沒變爲0的話就會繼續執行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //將count值減1 public void countDown() { };
CountDownLatch countDown = new CountDownLatch(2)
countDown.countDown()
countDown.await()
實現阻塞同步package cn.day13; import java.util.concurrent.CountDownLatch; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub final CountDownLatch latch = new CountDownLatch(2); new Thread() { public void run() { try { System.out.println("子線程" + Thread.currentThread().getName() + "正在執行"); Thread.sleep(3000); System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread() { public void run() { try { System.out.println("子線程" + Thread.currentThread().getName() + "正在執行"); Thread.sleep(3000); System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); try { System.out.println("等待2個子線程執行完畢..."); latch.await(); System.out.println("2個子線程已經執行完畢"); System.out.println("繼續執行主線程"); } catch (InterruptedException e) { e.printStackTrace(); } } }
打印結果:併發
子線程Thread-0正在執行 等待2個子線程執行完畢... 子線程Thread-1正在執行 子線程Thread-0執行完畢 子線程Thread-1執行完畢 2個子線程已經執行完畢 繼續執行主線程
前面給了一個demo演示如何用,那這個東西在實際的業務場景中是否會用到呢?高併發
由於確實在一個業務場景中使用到了,否則也就不會單獨撈出這一節...工具
電商的詳情頁,由衆多的數據拼裝組成,如能夠分紅一下幾個模塊
上面的幾個模塊信息,都是從不一樣的服務獲取信息,且彼此沒啥關聯;因此爲了提升響應,徹底能夠作成併發獲取數據,如
可是最終拼裝數據並返回給前端,須要等到上面的全部信息都獲取完畢以後,才能返回,這個場景就很是的適合 CountDownLatch
來作了
CountDownLatch#await(long, TimeUnit)
等待全部的模塊信息返回CountDownLatch#countDown()
進行計數-1 CountDownLatch在多線程併發編程中充當一個計時器的功能,而且內部維護一個count的變量,而且其操做都是原子操做,該類主要經過countDown()和await()兩個方法實現功能的,首先經過創建CountDownLatch對象,而且傳入參數即爲count初始值。
若是一個線程調用了await()方法,那麼這個線程便進入阻塞狀態,並進入阻塞隊列。若是一個線程調用了countDown()方法,則會使count-1;當count的值爲0時,這時候阻塞隊列中調用await()方法的線程便會逐個被喚醒,從而進入後續的操做。好比下面的例子就是有兩個操做,一個是讀操做一個是寫操做,如今規定必須進行完寫操做才能進行讀操做。因此當最開始調用讀操做時,須要用await()方法使其阻塞,當寫操做結束時,則須要使count等於0。所以count的初始值能夠定爲寫操做的記錄數,這樣即可以使得進行完寫操做,而後進行讀操做。
內部也是有個Sync
類繼承了AQS
,因此CountDownLatch
類的構造方法就是調用Sync
類的構造方法,而後調用setState()
方法設置AQS
中state
的值。
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } Sync(int count) { setState(count); }
該方法是使調用的線程阻塞住,直到state
的值爲0就放開全部阻塞的線程。實現會調用到AQS
中的acquireSharedInterruptibly()
方法,先判斷下是否被中斷,接着調用了tryAcquireShared()
方法,講AQS
那篇文章裏提到過這個方法是須要子類實現的,能夠看到實現的邏輯就是判斷state
值是否爲0,是就返回1,不是則返回-1。
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; }
這個方法會對state
值減1,會調用到AQS
中releaseShared()
方法,目的是爲了調用doReleaseShared()
方法,這個是AQS定義好的釋放資源的方法,而tryReleaseShared()
則是子類實現的,能夠看到是一個自旋CAS
操做,每次都獲取state
值,若是爲0則直接返回,不然就執行減1的操做,失敗了就重試,若是減完後值爲0就表示要釋放全部阻塞住的線程了,也就會執行到AQS
中的doReleaseShared()
方法。
public void countDown() { sync.releaseShared(1); } public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } 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; } }
瘋狂創客圈 - Java高併發研習社羣,爲你們開啓大廠之門