<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JUC并發(fā)編程之CountDownLatch源碼詳解

          共 17529字,需瀏覽 36分鐘

           ·

          2021-07-08 12:09


          點(diǎn)擊上方藍(lán)字 關(guān)注我吧



          1
          前言

          關(guān)于JUC包下的工具類,到目前為止已經(jīng)分享了ReentranLock、Semaphore這兩個(gè)工具類,同樣很多前置內(nèi)容在前面兩遍博文中也都要講到,那么今天所分享的是CountDownLatch工具類、通過前面博文我們知道ReentranLock是獨(dú)占鎖模式、Semaphore是共享鎖模式、那么CountDownLatch是什么模式呢?CountDownLatch它是閉鎖模式。


          什么是閉鎖?
          閉鎖可以延遲線程的進(jìn)度直到其到達(dá)終止?fàn)顟B(tài),閉鎖可以用來確保某些活動(dòng)直到其他活動(dòng)都完成才繼續(xù)執(zhí)行
          例如:
          1.確保某個(gè)服務(wù)在其依賴的其他服務(wù)都啟動(dòng)之后才啟動(dòng)
          2.等待某個(gè)操作的所有參與者都準(zhǔn)備就緒才繼續(xù)執(zhí)行


          2
          什么是CountDownLatch


          CountDownLatch能夠使一個(gè)線程在等待另外一些線程完成各自工作之后,再繼續(xù)執(zhí)行。使用一個(gè)計(jì)數(shù)器進(jìn)行實(shí)現(xiàn)。計(jì)數(shù)器初始值為線程的數(shù)量。當(dāng)每一個(gè)線程完成自己任務(wù)后,計(jì)數(shù)器的值就會(huì)減一。當(dāng)計(jì)數(shù)器的值為0時(shí),表示所有的線程都已經(jīng)完成一些任務(wù),然后在CountDownLatch上等待的線程就可以恢復(fù)執(zhí)行接下來的任務(wù)。


          如上這段話可能不是那么好理解,我舉一個(gè)生活中很通俗的例子:

          一家人要外出自駕游,旅游的家庭成員總共有5位,只有當(dāng)5位家庭成員全部上車了,爸爸才會(huì)開始開車出發(fā)對(duì)嗎?假如爸爸現(xiàn)在通知家庭成員要出發(fā)了,其他的3位成員陸陸續(xù)續(xù)的上車,此時(shí)車上已有4位成員了,
          還有一位小妹妹在上廁所,這個(gè)時(shí)候爸爸以及其他3位成員需要等待妹妹上車后,才會(huì)開車出發(fā)旅游。


          3
          CountDownLatch的使用場(chǎng)景


          在我們平常開發(fā)過程中,需要對(duì)某些接口進(jìn)行高平發(fā)測(cè)試,一般我們會(huì)想到通過jmeter性能工具進(jìn)行壓測(cè),但是我們有沒有java工具類去幫我實(shí)現(xiàn)這種并發(fā)測(cè)試的API工具呢?當(dāng)然是有的,就是CountDownLacth工具類,我們可以在代碼中模擬高并發(fā)測(cè)試接口的場(chǎng)景


          以及上述所說的自駕游場(chǎng)景,當(dāng)然大家也可以自己發(fā)揮想象,項(xiàng)目中是否有場(chǎng)景能夠用到該工具類。


          4
          CountDownLatch源碼詳解


          如何使用CountDownLatch
          聊完CountDownLatch的使用場(chǎng)景后,我們來看看基于上面的場(chǎng)景通過CountDownLatch來實(shí)現(xiàn)相應(yīng)的功能
          /** * @author sunny * @date 2021/07/06 09:30 * @description */@Slf4jpublic class CountDownLatchTest {    public static void main(String[] args) throws InterruptedException {        test01();//        test02();//        test03();    }    /**     * 模擬高并發(fā)場(chǎng)景     *     * @throws InterruptedException     */    public static void test01() throws InterruptedException {        CountDownLatch countDownLatch = new CountDownLatch(1);        for (int i = 0; i < 10; i++) {            new Thread(() -> {                try {                    log.info("{}:線程已就緒,當(dāng)前時(shí)間戳:{}", Thread.currentThread().getName(), System.currentTimeMillis());                    countDownLatch.await();                    log.info("{}:線程已釋放,,當(dāng)前時(shí)間戳:{}", Thread.currentThread().getName(), System.currentTimeMillis());                } catch (InterruptedException e) {                    e.printStackTrace();                }            }).start();        }        TimeUnit.SECONDS.sleep(3);        System.out.println("\n ========================= \n");        countDownLatch.countDown();    }    /**     * 模擬家庭旅游場(chǎng)景     */    public static void test02() {        CountDownLatch countDownLatch = new CountDownLatch(11);        for (int i = 0; i < 10; i++) {            new Thread(() -> {                log.info("{}:已經(jīng)上車了", Thread.currentThread().getName());                countDownLatch.countDown();            }).start();        }        new Thread(() -> {            try {                TimeUnit.SECONDS.sleep(5);                log.info("{}:五秒后已經(jīng)上車了", Thread.currentThread().getName());                countDownLatch.countDown();            } catch (InterruptedException e) {                e.printStackTrace();            }        }).start();        try {            TimeUnit.SECONDS.sleep(2);            System.out.println("\n ========================= \n");            countDownLatch.await();            log.info("開車旅游啦");        } catch (InterruptedException e) {            e.printStackTrace();        }    }    /**     * 模擬去峨眉山游玩,但是因?yàn)橹型居械缆沸枰蘼罚孕枰却尥旰蟛拍艹霭l(fā)     * 那么修路的過程中     */    public static void test03() throws InterruptedException {        CountDownLatch countDownLatch = new CountDownLatch(10);        for (int i = 1; i <= 9; i++) {            new Thread(() -> {                try {                    log.info("{}:線程,我要準(zhǔn)備前往峨眉山,路被堵住了", Thread.currentThread().getName());                    countDownLatch.await();                    log.info("{}:線程,道路終于可以通行了", Thread.currentThread().getName());                } catch (InterruptedException e) {                    e.printStackTrace();                }            }).start();        }        TimeUnit.SECONDS.sleep(3);        log.info("======================準(zhǔn)備開始動(dòng)工======================");        for (int i = 1; i <= 9; i++) {            Thread.sleep(300);            int fi = i;            new Thread(() -> {                countDownLatch.countDown();                log.info("{}:線程,已開工:{}天,剩余:{}天", Thread.currentThread().getName(), fi, countDownLatch.getCount());            }).start();        }        TimeUnit.SECONDS.sleep(6);        new Thread(() -> {            log.info("{}:線程,已開工", Thread.currentThread().getName());            countDownLatch.countDown();        }).start();        TimeUnit.SECONDS.sleep(8);        for (int i = 0; i < 5; i++) {            new Thread(() -> {                try {                    countDownLatch.await();                    log.info("{}:線程,去峨眉山游玩啦", Thread.currentThread().getName());                } catch (InterruptedException e) {                    e.printStackTrace();                }            }).start();        }    }}

          源碼分析
          CountDownLatch有一個(gè)構(gòu)造方法,傳入的參數(shù)給state進(jìn)行初始化,在CountDownLatch中state,我們就可以理解閉鎖值,例如上面所說的家庭自駕游案例,當(dāng)家庭成員全部到位,爸爸才會(huì)開車出發(fā)旅游對(duì)么?
          public CountDownLatch(int count) {    if (count < 0) throw new IllegalArgumentException("count < 0");    this.sync = new Sync(count);  // 更新 state 值}


          首先從 "countDownLatch.await()" 作為源碼入口
          public void await() throws InterruptedException {    sync.acquireSharedInterruptibly(1);}


          在該方法內(nèi)有兩個(gè)if判斷,首先判斷當(dāng)前線程是否被中斷,如果被中斷了則直接拋出異常,而tryAcquireShared()方法則是通用模板方法,不同的子類根據(jù)自己的特性實(shí)現(xiàn)具體的邏輯
          public final void acquireSharedInterruptibly(int arg)        throws InterruptedException {    if (Thread.interrupted())        throw new InterruptedException();    if (tryAcquireShared(arg) < 0)        doAcquireSharedInterruptibly(arg);}


          具體的加鎖邏輯由子類自身的特性去具體實(shí)現(xiàn)的,在CountDownLatch中,它的加鎖鉤子方法如下所示,如果不進(jìn)行重寫該方法,則強(qiáng)制拋出異常。
          protected int tryAcquireShared(int arg) {    throw new UnsupportedOperationException();}


          接著我們走到CountDownLatch所實(shí)現(xiàn)邏輯代碼塊,該方法很簡(jiǎn)單,就是一個(gè)三元表達(dá)式,如果當(dāng)前線程獲取的state為0則代表無需進(jìn)行等待,否則需要進(jìn)行入隊(duì)等待。就如剛剛前面所講的自駕游場(chǎng)景案例,只有當(dāng)所有成員全部上車了,才會(huì)開車出發(fā),到這里還是很好理解的對(duì)嗎?哈哈哈,我們接著往下看。
          protected int tryAcquireShared(int acquires) {    return (getState() == 0) ? 1 : -1;}


          然后我們回退到上一步,看到acquireSharedInterruptibly()方法,如果state大于0則返回-1,從而會(huì)進(jìn)入到doAcquireSharedInterruptibly()方法,這個(gè)方法與Semaphore()邏輯幾乎一樣,只是我們需要理解概念所一樣而已。
          public final void acquireSharedInterruptibly(int arg)        throws InterruptedException {    if (Thread.interrupted())        throw new InterruptedException();    if (tryAcquireShared(arg) < 0)        doAcquireSharedInterruptibly(arg);}


          然后我們進(jìn)入到doAcquireSharedInterruptibly()方法,主要的邏輯都在自旋里面,但是外面同樣也有個(gè)比較重要的方法,就是addWaiter()方法,該方法傳入的參數(shù)值為 "Node.SHARED" ,而SHARED的值就是new Node() 也就是創(chuàng)建了一個(gè)空的節(jié)點(diǎn),然后我們來看看addWaiter()方法其內(nèi)部邏輯做了些什么事情?
          private void doAcquireSharedInterruptibly(int arg)    throws InterruptedException {    final Node node = addWaiter(Node.SHARED);  // 構(gòu)建雙向鏈表 或 入隊(duì)操作    boolean failed = true;    try {        for (;;) { // 自旋            final Node p = node.predecessor();  //獲取當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)            if (p == head) {                int r = tryAcquireShared(arg);  // 嘗試獲取令牌                if (r >= 0) {  // 獲取令牌成功                    setHeadAndPropagate(node, r);  //傳播鏈表                    p.next = null; // help GC    將前驅(qū)節(jié)點(diǎn)的引用指向?yàn)镹ULL,待垃圾回收器回收                    failed = false;                    return;  // 獲取令牌成功,退出自旋                }            }            if (shouldParkAfterFailedAcquire(p, node) &&                parkAndCheckInterrupt())   // 阻塞當(dāng)前線程                throw new InterruptedException();        }    } finally {        // 如果某個(gè)線程被中斷,非正常流程退出則將當(dāng)前線程的節(jié)點(diǎn)設(shè)置為cancel狀態(tài)        if (failed)            cancelAcquire(node);    }}


          使文字更好理解代碼這里先做前綴說明,node = 當(dāng)前節(jié)點(diǎn),tail = 鏈表末尾節(jié)點(diǎn),head = 鏈表頭節(jié)點(diǎn)
          首先將當(dāng)前線程封裝為node節(jié)點(diǎn),接著獲取tail節(jié)點(diǎn),判斷當(dāng)前AQS中是否存在雙向鏈表,如果存在的話,將node前驅(qū)節(jié)點(diǎn)引用指向tail節(jié)點(diǎn),通過cas將node節(jié)點(diǎn)設(shè)置為末尾節(jié)點(diǎn),如果設(shè)置成功則將tail節(jié)點(diǎn)的后驅(qū)引用指向node,那么node就順理成章的成了雙向鏈表的末尾節(jié)點(diǎn)了。關(guān)于這里我們其實(shí)需要思考一個(gè)問題,在多線程情況下同時(shí)通過cas去設(shè)置尾節(jié)點(diǎn),此時(shí)只會(huì)有一個(gè)線程設(shè)置成功且返回出去,那接下來的線程該怎么辦呢?且不急,帶著這個(gè)疑問我們進(jìn)入到enq方法
          private Node addWaiter(Node mode) {    Node node = new Node(Thread.currentThread(), mode);   // 封裝節(jié)點(diǎn)    // Try the fast path of enq; backup to full enq on failure    Node pred = tail;  // 獲取末尾節(jié)點(diǎn)    if (pred != null) {        node.prev = pred;   // 當(dāng)前節(jié)點(diǎn)的前驅(qū)引用指向?yàn)閜red        if (compareAndSetTail(pred, node)) {  // 將當(dāng)前節(jié)點(diǎn)設(shè)置為鏈表末尾節(jié)點(diǎn)            pred.next = node;  // 原末尾節(jié)點(diǎn)后驅(qū)引用指向?yàn)楫?dāng)前節(jié)點(diǎn)            return node;         }    }    enq(node);    return node;}


          基于FIFO入隊(duì)流程圖


          通過如下圖理解上面這段話,我相信應(yīng)該是能夠明白的



          使文字更好理解代碼這里先做前綴說明,node = 當(dāng)前節(jié)點(diǎn),tail = 鏈表末尾節(jié)點(diǎn),head = 鏈表頭節(jié)點(diǎn)
          得勒,進(jìn)來就是一層自旋,注意這里的精華就是自旋,以及上面所提到多線程通過cas設(shè)置尾節(jié)點(diǎn)失敗的解決方案就在此方法。
          進(jìn)入自旋獲取鏈表的末尾節(jié)點(diǎn),如果獲取tail為null則證明當(dāng)前并沒有構(gòu)成雙向鏈表,接著通過cas去設(shè)置head,然后將head指向tail,這樣雙向鏈表就完成了,如果獲取tail不為null,將node前驅(qū)引用指向tail節(jié)點(diǎn),然后tail的后驅(qū)節(jié)點(diǎn)引用指向node節(jié)點(diǎn),然后返回出去。那如果設(shè)置失敗了怎么辦呢?回到上面的問題,問題不大,這方法不是自旋嘛,它會(huì)一直自旋到你設(shè)置成功為止,才退出自旋。
          private Node enq(final Node node) {    for (;;) {        Node t = tail; // 獲取末尾節(jié)點(diǎn)        if (t == null) { // Must initialize   // 構(gòu)建雙向鏈表            if (compareAndSetHead(new Node()))                tail = head;        } else {            node.prev = t;            if (compareAndSetTail(t, node)) {                t.next = node;                return t;            }        }    }}


          如果通過cas設(shè)置不成功,就一直進(jìn)行自旋,直到設(shè)置成功才退出循環(huán)。



          接著,回退到doAcquireSharedInterruptibly()方法,通過上面的流程下來,我們就知道node節(jié)點(diǎn)現(xiàn)在已經(jīng)成功入隊(duì)到雙向鏈表中,接著判斷如果當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是為頭節(jié)點(diǎn)此時(shí)會(huì)嘗試獲取令牌,如果獲取失敗則將線程進(jìn)行阻塞,同理當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)不是鏈表的頭節(jié)點(diǎn),也會(huì)將當(dāng)前線程進(jìn)行阻塞。無論如何只要令牌沒有了,就得老老實(shí)實(shí)的在隊(duì)列中進(jìn)行呆著,直到下一次的喚醒。
          那如果線程為頭節(jié)點(diǎn)且獲取令牌成功了,setHeadAndPropagate()方法又會(huì)做些什么事情呢?帶著這個(gè)疑問,我們進(jìn)去一探究竟
          private void doAcquireSharedInterruptibly(int arg)    throws InterruptedException {    final Node node = addWaiter(Node.SHARED);  // 構(gòu)建雙向鏈表 或 入隊(duì)操作    boolean failed = true;    try {        for (;;) { // 自旋            final Node p = node.predecessor();  //獲取當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)            if (p == head) {                int r = tryAcquireShared(arg);  // 嘗試獲取令牌                if (r >= 0) {  // 獲取令牌成功                    setHeadAndPropagate(node, r);  //傳播鏈表                    p.next = null; // help GC    將前驅(qū)節(jié)點(diǎn)的引用指向?yàn)镹ULL,待垃圾回收器回收                    failed = false;                    return;  // 獲取令牌成功,退出自旋                }            }            if (shouldParkAfterFailedAcquire(p, node) && //判斷線程是否需要被阻塞                parkAndCheckInterrupt())   // 阻塞當(dāng)前線程                throw new InterruptedException();        }    } finally {        // 如果某個(gè)線程被中斷,非正常流程退出則將當(dāng)前線程的節(jié)點(diǎn)設(shè)置為cancel狀態(tài)        if (failed)            cancelAcquire(node);    }}


          首先我們看到該方法的入?yún)?nèi)容,node:當(dāng)前獲取令牌線程節(jié)點(diǎn),propagate: 值是根據(jù)獲取state是否等于0判斷,如果等0這么為1否則為-1
          該方法主要作用在于兩點(diǎn),第一點(diǎn):將當(dāng)前節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn),第二點(diǎn):自動(dòng)喚醒下一個(gè)節(jié)點(diǎn)
          private void setHeadAndPropagate(Node node, int propagate) {    Node h = head; // Record old head for check below    setHead(node);    /*     * Try to signal next queued node if:     *   Propagation was indicated by caller,     *     or was recorded (as h.waitStatus either before     *     or after setHead) by a previous operation     *     (note: this uses sign-check of waitStatus because     *      PROPAGATE status may transition to SIGNAL.)     * and     *   The next node is waiting in shared mode,     *     or we don't know, because it appears null     *     * The conservatism in both of these checks may cause     * unnecessary wake-ups, but only when there are multiple     * racing acquires/releases, so most need signals now or soon     * anyway.     */    if (propagate > 0 || h == null || h.waitStatus < 0 ||   // 還有令牌可獲取 || 頭節(jié)點(diǎn)狀態(tài)處于等待狀態(tài)        (h = head) == null || h.waitStatus < 0) {        Node s = node.next;  // 獲取當(dāng)前下一節(jié)點(diǎn)        if (s == null || s.isShared())  // 判斷下節(jié)點(diǎn)是否為共享節(jié)點(diǎn)            doReleaseShared();  // 傳播~~ 具體傳播什么呢???    }}

          稍微可以看下設(shè)置頭節(jié)點(diǎn)方法,也就是出隊(duì)操作,主要就是將當(dāng)前線程設(shè)置為頭節(jié)點(diǎn),然后將當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)引用指向?yàn)閚ull,配合方法外,會(huì)將之前的頭節(jié)點(diǎn)的next節(jié)點(diǎn)設(shè)置為null,那么之前的頭節(jié)點(diǎn)也就自然會(huì)被垃圾回收器進(jìn)行
          private void setHead(Node node) {    head = node;    node.thread = null;    node.prev = null;}


          基于FIFO出隊(duì)流程圖



          又一次來到自旋,首先驗(yàn)證鏈表中是否還存在多個(gè)節(jié)點(diǎn),如果存在且狀態(tài)為SIGNAL會(huì)將head的后驅(qū)節(jié)點(diǎn)進(jìn)行喚醒。這里沒啥太多好說的,就是一個(gè)傳播概念,當(dāng)你有多個(gè)節(jié)點(diǎn)在阻塞中,當(dāng)state為0,是不是我的所有阻塞節(jié)點(diǎn)都需要被喚醒,然后執(zhí)行后續(xù)的邏輯對(duì)么?
          private void doReleaseShared() {    /*     * Ensure that a release propagates, even if there are other     * in-progress acquires/releases.  This proceeds in the usual     * way of trying to unparkSuccessor of head if it needs     * signal. But if it does not, status is set to PROPAGATE to     * ensure that upon release, propagation continues.     * Additionally, we must loop in case a new node is added     * while we are doing this. Also, unlike other uses of     * unparkSuccessor, we need to know if CAS to reset status     * fails, if so rechecking.     */    for (;;) {  // 自旋   可以理解為傳播 【加自旋的原因,可能同時(shí)有多個(gè)令牌被釋放,那么在這里就可以喚醒后續(xù)所有節(jié)點(diǎn)去獲取令牌,就不用在前面再去判斷是否要去喚醒后驅(qū)節(jié)點(diǎn)了。 如果沒有獲取到令牌也沒關(guān)系,后面還是會(huì)將沒有搶到的線程進(jìn)行阻塞住】        Node h = head;          if (h != null && h != tail) {  // 頭節(jié)點(diǎn)不為null 其 頭非等于尾節(jié)點(diǎn) 則證明當(dāng)前鏈表還有多個(gè)節(jié)點(diǎn)            int ws = h.waitStatus;   // 獲取head的節(jié)點(diǎn)狀態(tài)            if (ws == Node.SIGNAL) {  // 如果當(dāng)前節(jié)點(diǎn)狀態(tài)為SIGNAL,就代表后驅(qū)節(jié)點(diǎn)正在被阻塞著                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))  // 通過cas將狀態(tài)從等待更換為非等待,然后取反的話,將下一個(gè)節(jié)點(diǎn)喚醒                    continue;            // loop to recheck cases                unparkSuccessor(h);  // 喚醒線程 去獲取令牌            }            else if (ws == 0 &&                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))  // 如果節(jié)點(diǎn)狀態(tài)已經(jīng)為0,則會(huì)將節(jié)點(diǎn)的狀態(tài)更新為PROPAGATE   PROPAGATE:表示下一次共享式同步狀態(tài)獲取將會(huì)被無條件地傳播下去                continue;                // loop on failed CAS        }        if (h == head)                   // loop if head changed            break;   // 跳出當(dāng)前循環(huán)    }}

          unparkSuccessor()方法很簡(jiǎn)單,在正常流程下它只會(huì)通過LockSupport.unpark(),將下一節(jié)點(diǎn)進(jìn)行喚醒
          private void unparkSuccessor(Node node) {    // 先獲取head節(jié)點(diǎn)的狀態(tài),應(yīng)該是等于-1,原因在shouldParkAfterFailedAcquire方法中有體現(xiàn)    int ws = node.waitStatus;        // 由于-1會(huì)小于0,所以更新改為0    if (ws < 0)        compareAndSetWaitStatus(node, ws, 0);
          // 獲取第一個(gè)正常排隊(duì)的節(jié)點(diǎn) Node s = node.next; //正常解鎖流程不會(huì)走該if判斷 if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } // 正常來說第一個(gè)排隊(duì)的節(jié)點(diǎn)不應(yīng)該為空,所以直接把第一個(gè)排隊(duì)的線程喚醒 if (s != null) LockSupport.unpark(s.thread);}


          接下來看看countDown()方法到底也做了些什么流程操作
          首先從 "countDownLatch.countDown()" 作為源碼入口
          sync.releaseShared(1);


          我們來看到releaseShared方法,該方法內(nèi)部有兩個(gè)核心方法,我們先進(jìn)入看看tryReleaseShared做了些什么事情
          public final boolean releaseShared(int arg) {    if (tryReleaseShared(arg)) {  //通用釋放令牌        doReleaseShared();  //喚醒后驅(qū)節(jié)點(diǎn)        return true;    }    return false;}


          我們又看到了自旋,判斷當(dāng)前的state值是否等于0,等于0則代表需要提前的準(zhǔn)備的線程都已就緒,主線程也可以執(zhí)行剩下的業(yè)務(wù)邏輯啦,那如果不為0怎么辦?一直自減,直到減到state為0,然后將鏈表內(nèi)的線程全部進(jìn)行喚醒。也就是會(huì)走到我上面所說到的doReleaseShared()方法
          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;    }}

          那么到這CountDownLatch源碼分析到此結(jié)束了,相信大家伙如果看過我前面兩篇文章,再看這篇博文會(huì)發(fā)現(xiàn)理解起來非常簡(jiǎn)單的。


          JUC并發(fā)編程之CountDownLatch源碼講解視頻


          我是黎明大大,我知道我沒有驚世的才華,也沒有超于凡人的能力,但畢竟我還有一個(gè)不屈服,敢于選擇向命運(yùn)沖鋒的靈魂,和一個(gè)就是傷痕累累也要義無反顧走下去的心。


          如果您覺得本文對(duì)您有幫助,還請(qǐng)關(guān)注點(diǎn)贊一波,后期將不間斷更新更多技術(shù)文章


          掃描二維碼關(guān)注我
          不定期更新技術(shù)文章哦



          JUC并發(fā)編程之Semaphore源碼詳解

          JUC并發(fā)編程之ReentrantLock非公平鎖源碼詳解

          JUC并發(fā)編程之Synchronized關(guān)鍵字詳解

          JUC并發(fā)編程之MESI緩存一致協(xié)議詳解

          JUC并發(fā)編程之Volatile關(guān)鍵字詳解

          JUC并發(fā)編程之JMM內(nèi)存模型詳解

          深入Hotspot源碼與Linux內(nèi)核理解NIO與Epoll



          發(fā)現(xiàn)“在看”和“贊”了嗎,因?yàn)槟愕狞c(diǎn)贊,讓我元?dú)鉂M滿哦
          瀏覽 63
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天日夜夜操B | 一级日韩| 黄片大鸡巴 | 日本亲子乱婬一级A片 | 四虎av在线 |