成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一文搞懂 CountDownLatch 用法和源碼!

開(kāi)發(fā) 前端
本文是 CountDownLatch 的基本使用和源碼分析,CountDownLatch 就是一個(gè)基于 AQS 的計(jì)數(shù)器,它內(nèi)部的方法都是圍繞 AQS 框架來(lái)談的,除此之外還有其他比如 ReentrantLock、Semaphore 等都是 AQS 的實(shí)現(xiàn),所以要研究并發(fā)的話(huà),離不開(kāi)對(duì) AQS 的探討。

 [[358879]]

CountDownLatch 是多線(xiàn)程控制的一種工具,它被稱(chēng)為 門(mén)閥、 計(jì)數(shù)器或者 閉鎖。這個(gè)工具經(jīng)常用來(lái)用來(lái)協(xié)調(diào)多個(gè)線(xiàn)程之間的同步,或者說(shuō)起到線(xiàn)程之間的通信(而不是用作互斥的作用)。下面我們就來(lái)一起認(rèn)識(shí)一下 CountDownLatch

認(rèn)識(shí) CountDownLatch

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

CountDownLatch 的使用

CountDownLatch 提供了一個(gè)構(gòu)造方法,你必須指定其初始值,還指定了 countDown 方法,這個(gè)方法的作用主要用來(lái)減小計(jì)數(shù)器的值,當(dāng)計(jì)數(shù)器變?yōu)?0 時(shí),在 CountDownLatch 上 await 的線(xiàn)程就會(huì)被喚醒,繼續(xù)執(zhí)行其他任務(wù)。當(dāng)然也可以延遲喚醒,給 CountDownLatch 加一個(gè)延遲時(shí)間就可以實(shí)現(xiàn)。

其主要方法如下

CountDownLatch 主要有下面這幾個(gè)應(yīng)用場(chǎng)景

CountDownLatch 應(yīng)用場(chǎng)景

典型的應(yīng)用場(chǎng)景就是當(dāng)一個(gè)服務(wù)啟動(dòng)時(shí),同時(shí)會(huì)加載很多組件和服務(wù),這時(shí)候主線(xiàn)程會(huì)等待組件和服務(wù)的加載。當(dāng)所有的組件和服務(wù)都加載完畢后,主線(xiàn)程和其他線(xiàn)程在一起完成某個(gè)任務(wù)。

CountDownLatch 還可以實(shí)現(xiàn)學(xué)生一起比賽跑步的程序,CountDownLatch 初始化為學(xué)生數(shù)量的線(xiàn)程,鳴槍后,每個(gè)學(xué)生就是一條線(xiàn)程,來(lái)完成各自的任務(wù),當(dāng)?shù)谝粋€(gè)學(xué)生跑完全程后,CountDownLatch 就會(huì)減一,直到所有的學(xué)生完成后,CountDownLatch 會(huì)變?yōu)?0 ,接下來(lái)再一起宣布跑步成績(jī)。

順著這個(gè)場(chǎng)景,你自己就可以延伸、拓展出來(lái)很多其他任務(wù)場(chǎng)景。

CountDownLatch 用法

下面我們通過(guò)一個(gè)簡(jiǎn)單的計(jì)數(shù)器來(lái)演示一下 CountDownLatch 的用法

  1. public class TCountDownLatch { 
  2.  
  3.     public static void main(String[] args) { 
  4.         CountDownLatch latch = new CountDownLatch(5); 
  5.         Increment increment = new Increment(latch); 
  6.         Decrement decrement = new Decrement(latch); 
  7.  
  8.         new Thread(increment).start(); 
  9.         new Thread(decrement).start(); 
  10.  
  11.         try { 
  12.             Thread.sleep(6000); 
  13.         } catch (InterruptedException e) { 
  14.             e.printStackTrace(); 
  15.         } 
  16.     } 
  17.  
  18. class Decrement implements Runnable { 
  19.  
  20.     CountDownLatch countDownLatch; 
  21.  
  22.     public Decrement(CountDownLatch countDownLatch){ 
  23.         this.countDownLatch = countDownLatch; 
  24.     } 
  25.  
  26.     @Override 
  27.     public void run() { 
  28.         try { 
  29.  
  30.             for(long i = countDownLatch.getCount();i > 0;i--){ 
  31.                 Thread.sleep(1000); 
  32.                 System.out.println("countdown"); 
  33.                 this.countDownLatch.countDown(); 
  34.             } 
  35.  
  36.         } catch (InterruptedException e) { 
  37.             e.printStackTrace(); 
  38.         } 
  39.     } 
  40.  
  41.  
  42. class Increment implements Runnable { 
  43.  
  44.     CountDownLatch countDownLatch; 
  45.  
  46.     public Increment(CountDownLatch countDownLatch){ 
  47.         this.countDownLatch = countDownLatch; 
  48.     } 
  49.  
  50.     @Override 
  51.     public void run() { 
  52.         try { 
  53.             System.out.println("await"); 
  54.             countDownLatch.await(); 
  55.         } catch (InterruptedException e) { 
  56.             e.printStackTrace(); 
  57.         } 
  58.         System.out.println("Waiter Released"); 
  59.     } 

在 main 方法中我們初始化了一個(gè)計(jì)數(shù)器為 5 的 CountDownLatch,在 Decrement 方法中我們使用 countDown 執(zhí)行減一操作,然后睡眠一段時(shí)間,同時(shí)在 Increment 類(lèi)中進(jìn)行等待,直到 Decrement 中的線(xiàn)程完成計(jì)數(shù)減一的操作后,喚醒 Increment 類(lèi)中的 run 方法,使其繼續(xù)執(zhí)行。

下面我們?cè)賮?lái)通過(guò)學(xué)生賽跑這個(gè)例子來(lái)演示一下 CountDownLatch 的具體用法

  1. public class StudentRunRace { 
  2.  
  3.     CountDownLatch stopLatch = new CountDownLatch(1); 
  4.     CountDownLatch runLatch = new CountDownLatch(10); 
  5.  
  6.     public void waitSignal() throws Exception{ 
  7.         System.out.println("選手" + Thread.currentThread().getName() + "正在等待裁判發(fā)布口令"); 
  8.         stopLatch.await(); 
  9.         System.out.println("選手" + Thread.currentThread().getName() + "已接受裁判口令"); 
  10.         Thread.sleep((long) (Math.random() * 10000)); 
  11.         System.out.println("選手" + Thread.currentThread().getName() + "到達(dá)終點(diǎn)"); 
  12.         runLatch.countDown(); 
  13.     } 
  14.  
  15.     public void waitStop() throws Exception{ 
  16.         Thread.sleep((long) (Math.random() * 10000)); 
  17.         System.out.println("裁判"+Thread.currentThread().getName()+"即將發(fā)布口令"); 
  18.         stopLatch.countDown(); 
  19.         System.out.println("裁判"+Thread.currentThread().getName()+"已發(fā)送口令,正在等待所有選手到達(dá)終點(diǎn)"); 
  20.         runLatch.await(); 
  21.         System.out.println("所有選手都到達(dá)終點(diǎn)"); 
  22.         System.out.println("裁判"+Thread.currentThread().getName()+"匯總成績(jī)排名"); 
  23.     } 
  24.  
  25.     public static void main(String[] args) { 
  26.         ExecutorService service = Executors.newCachedThreadPool(); 
  27.         StudentRunRace studentRunRace = new StudentRunRace(); 
  28.         for (int i = 0; i < 10; i++) { 
  29.             Runnable runnable = () -> { 
  30.                 try { 
  31.                     studentRunRace.waitSignal(); 
  32.                 } catch (Exception e) { 
  33.                     e.printStackTrace(); 
  34.                 } 
  35.             }; 
  36.             service.execute(runnable); 
  37.         } 
  38.         try { 
  39.             studentRunRace.waitStop(); 
  40.         } catch (Exception e) { 
  41.             e.printStackTrace(); 
  42.         } 
  43.         service.shutdown(); 
  44.     } 

下面我們就來(lái)一起分析一下 CountDownLatch 的源碼

CountDownLatch 源碼分析

CountDownLatch 使用起來(lái)比較簡(jiǎn)單,但是卻非常有用,現(xiàn)在你可以在你的工具箱中加上 CountDownLatch 這個(gè)工具類(lèi)了。下面我們就來(lái)深入認(rèn)識(shí)一下 CountDownLatch。

CountDownLatch 的底層是由 AbstractQueuedSynchronizer 支持,而 AQS 的數(shù)據(jù)結(jié)構(gòu)的核心就是兩個(gè)隊(duì)列,一個(gè)是 同步隊(duì)列(sync queue),一個(gè)是條件隊(duì)列(condition queue)。

Sync 內(nèi)部類(lèi)

CountDownLatch 在其內(nèi)部是一個(gè) Sync ,它繼承了 AQS 抽象類(lèi)。

  1. private static final class Sync extends AbstractQueuedSynchronizer {...} 

CountDownLatch 其實(shí)其內(nèi)部只有一個(gè) sync 屬性,并且是 final 的

  1. private final Sync sync; 

CountDownLatch 只有一個(gè)帶參數(shù)的構(gòu)造方法

  1. public CountDownLatch(int count) { 
  2.   if (count < 0) throw new IllegalArgumentException("count < 0"); 
  3.   this.sync = new Sync(count); 

也就是說(shuō),初始化的時(shí)候必須指定計(jì)數(shù)器的數(shù)量,如果數(shù)量為負(fù)會(huì)直接拋出異常。

然后把 count 初始化為 Sync 內(nèi)部的 count,也就是

  1. Sync(int count) { 
  2.   setState(count); 

注意這里有一個(gè) setState(count),這是什么意思呢?見(jiàn)聞知意這只是一個(gè)設(shè)置狀態(tài)的操作,但是實(shí)際上不單單是,還有一層意思是 state 的值代表著待達(dá)到條件的線(xiàn)程數(shù)。這個(gè)我們?cè)诹?countDown 方法的時(shí)候再討論。

getCount() 方法的返回值是 getState() 方法,它是 AbstractQueuedSynchronizer 中的方法,這個(gè)方法會(huì)返回當(dāng)前線(xiàn)程計(jì)數(shù),具有 volatile 讀取的內(nèi)存語(yǔ)義。

  1. // ---- CountDownLatch ---- 
  2.  
  3. int getCount() { 
  4.   return getState(); 
  5.  
  6. // ---- AbstractQueuedSynchronizer ---- 
  7.  
  8. protected final int getState() { 
  9.   return state; 

tryAcquireShared() 方法用于獲取·共享狀態(tài)下對(duì)象的狀態(tài),判斷對(duì)象是否為 0 ,如果為 0 返回 1 ,表示能夠嘗試獲取,如果不為 0,那么返回 -1,表示無(wú)法獲取。

  1. protected int tryAcquireShared(int acquires) { 
  2.   return (getState() == 0) ? 1 : -1; 
  3.  
  4. // ----  getState() 方法和上面的方法相同 ---- 

這個(gè) 共享狀態(tài) 屬于 AQS 中的概念,在 AQS 中分為兩種模式,一種是 獨(dú)占模式,一種是 共享模式。

  • tryAcquire 獨(dú)占模式,嘗試獲取資源,成功則返回 true,失敗則返回 false。
  • tryAcquireShared 共享方式,嘗試獲取資源。負(fù)數(shù)表示失敗;0 表示成功,但沒(méi)有剩余可用資源;正數(shù)表示成功,且有剩余資源。

tryReleaseShared() 方法用于共享模式下的釋放

  1. protected boolean tryReleaseShared(int releases) { 
  2.   // 減小數(shù)量,變?yōu)?nbsp;0 的時(shí)候進(jìn)行通知。 
  3.   for (;;) { 
  4.     int c = getState(); 
  5.     if (c == 0) 
  6.       return false
  7.     int nextc = c-1; 
  8.     if (compareAndSetState(c, nextc)) 
  9.       return nextc == 0; 
  10.   } 

這個(gè)方法是一個(gè)無(wú)限循環(huán),獲取線(xiàn)程狀態(tài),如果線(xiàn)程狀態(tài)是 0 則表示沒(méi)有被線(xiàn)程占有,沒(méi)有占有的話(huà)那么直接返回 false ,表示已經(jīng)釋放;然后下一個(gè)狀態(tài)進(jìn)行 - 1 ,使用 compareAndSetState CAS 方法進(jìn)行和內(nèi)存值的比較,如果內(nèi)存值也是 1 的話(huà),就會(huì)更新內(nèi)存值為 0 ,判斷 nextc 是否為 0 ,如果 CAS 比較不成功的話(huà),會(huì)再次進(jìn)行循環(huán)判斷。

await 方法

await() 方法是 CountDownLatch 一個(gè)非常重要的方法,基本上可以說(shuō)只有 countDown 和 await 方法才是 CountDownLatch 的精髓所在,這個(gè)方法將會(huì)使當(dāng)前線(xiàn)程在 CountDownLatch 計(jì)數(shù)減至零之前一直等待,除非線(xiàn)程被中斷。

CountDownLatch 中的 await 方法有兩種,一種是不帶任何參數(shù)的 await(),一種是可以等待一段時(shí)間的await(long timeout, TimeUnit unit)。下面我們先來(lái)看一下 await() 方法。

  1. public void await() throws InterruptedException { 
  2.   sync.acquireSharedInterruptibly(1); 

await 方法內(nèi)部會(huì)調(diào)用 acquireSharedInterruptibly 方法,這個(gè) acquireSharedInterruptibly 是 AQS 中的方法,以共享模式進(jìn)行中斷。

  1. public final void acquireSharedInterruptibly(int arg) 
  2.   throws InterruptedException { 
  3.   if (Thread.interrupted()) 
  4.     throw new InterruptedException(); 
  5.   if (tryAcquireShared(arg) < 0) 
  6.     doAcquireSharedInterruptibly(arg); 

可以看到,acquireSharedInterruptibly 方法的內(nèi)部會(huì)首先判斷線(xiàn)程是否中斷,如果線(xiàn)程中斷,則直接拋出線(xiàn)程中斷異常。如果沒(méi)有中斷,那么會(huì)以共享的方式獲取。如果能夠在共享的方式下不能獲取鎖,那么就會(huì)以共享的方式斷開(kāi)鏈接。

  1. private void doAcquireSharedInterruptibly(int arg) 
  2.   throws InterruptedException { 
  3.   final Node node = addWaiter(Node.SHARED); 
  4.   boolean failed = true
  5.   try { 
  6.     for (;;) { 
  7.       final Node p = node.predecessor(); 
  8.       if (p == head) { 
  9.         int r = tryAcquireShared(arg); 
  10.         if (r >= 0) { 
  11.           setHeadAndPropagate(node, r); 
  12.           p.next = null; // help GC 
  13.           failed = false
  14.           return
  15.         } 
  16.       } 
  17.       if (shouldParkAfterFailedAcquire(p, node) && 
  18.           parkAndCheckInterrupt()) 
  19.         throw new InterruptedException(); 
  20.     } 
  21.   } finally { 
  22.     if (failed) 
  23.       cancelAcquire(node); 
  24.   } 

這個(gè)方法有些長(zhǎng),我們分開(kāi)來(lái)看

  • 首先,會(huì)先構(gòu)造一個(gè)共享模式的 Node 入隊(duì)
  • 然后使用無(wú)限循環(huán)判斷新構(gòu)造 node 的前驅(qū)節(jié)點(diǎn),如果 node 節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn),那么就會(huì)判斷線(xiàn)程的狀態(tài),這里調(diào)用了一個(gè) setHeadAndPropagate ,其源碼如下
  1. private void setHeadAndPropagate(Node node, int propagate) { 
  2.   Node h = head;  
  3.   setHead(node); 
  4.   if (propagate > 0 || h == null || h.waitStatus < 0 || 
  5.       (h = head) == null || h.waitStatus < 0) { 
  6.     Node s = node.next
  7.     if (s == null || s.isShared()) 
  8.       doReleaseShared(); 
  9.   } 

首先會(huì)設(shè)置頭節(jié)點(diǎn),然后進(jìn)行一系列的判斷,獲取節(jié)點(diǎn)的獲取節(jié)點(diǎn)的后繼,以共享模式進(jìn)行釋放,就會(huì)調(diào)用 doReleaseShared 方法,我們?cè)賮?lái)看一下 doReleaseShared 方法

  1. private void doReleaseShared() { 
  2.  
  3.   for (;;) { 
  4.     Node h = head; 
  5.     if (h != null && h != tail) { 
  6.       int ws = h.waitStatus; 
  7.       if (ws == Node.SIGNAL) { 
  8.         if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 
  9.           continue;            // loop to recheck cases 
  10.         unparkSuccessor(h); 
  11.       } 
  12.       else if (ws == 0 && 
  13.                !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 
  14.         continue;                // loop on failed CAS 
  15.     } 
  16.     if (h == head)                   // loop if head changed 
  17.       break; 
  18.   } 

這個(gè)方法會(huì)以無(wú)限循環(huán)的方式首先判斷頭節(jié)點(diǎn)是否等于尾節(jié)點(diǎn),如果頭節(jié)點(diǎn)等于尾節(jié)點(diǎn)的話(huà),就會(huì)直接退出。如果頭節(jié)點(diǎn)不等于尾節(jié)點(diǎn),會(huì)判斷狀態(tài)是否為 SIGNAL,不是的話(huà)就繼續(xù)循環(huán) compareAndSetWaitStatus,然后斷開(kāi)后繼節(jié)點(diǎn)。如果狀態(tài)不是 SIGNAL,也會(huì)調(diào)用 compareAndSetWaitStatus 設(shè)置狀態(tài)為 PROPAGATE,狀態(tài)為 0 并且不成功,就會(huì)繼續(xù)循環(huán)。

也就是說(shuō) setHeadAndPropagate 就是設(shè)置頭節(jié)點(diǎn)并且釋放后繼節(jié)點(diǎn)的一系列過(guò)程。

  • 我們來(lái)看下面的 if 判斷,也就是 shouldParkAfterFailedAcquire(p, node) 這里
  1. if (shouldParkAfterFailedAcquire(p, node) && 
  2.     parkAndCheckInterrupt()) 
  3.   throw new InterruptedException(); 

如果上面 Node p = node.predecessor() 獲取前驅(qū)節(jié)點(diǎn)不是頭節(jié)點(diǎn),就會(huì)進(jìn)行 park 斷開(kāi)操作,判斷此時(shí)是否能夠斷開(kāi),判斷的標(biāo)準(zhǔn)如下

  1. private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { 
  2.   int ws = pred.waitStatus; 
  3.   if (ws == Node.SIGNAL) 
  4.     return true
  5.   if (ws > 0) { 
  6.     do { 
  7.       node.prev = pred = pred.prev; 
  8.     } while (pred.waitStatus > 0); 
  9.     pred.next = node; 
  10.   } else { 
  11.     compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 
  12.   } 
  13.   return false

這個(gè)方法會(huì)判斷 Node p 的前驅(qū)節(jié)點(diǎn)的結(jié)點(diǎn)狀態(tài)(waitStatus),節(jié)點(diǎn)狀態(tài)一共有五種,分別是

  1. CANCELLED(1):表示當(dāng)前結(jié)點(diǎn)已取消調(diào)度。當(dāng)超時(shí)或被中斷(響應(yīng)中斷的情況下),會(huì)觸發(fā)變更為此狀態(tài),進(jìn)入該狀態(tài)后的結(jié)點(diǎn)將不會(huì)再變化。
  2. SIGNAL(-1):表示后繼結(jié)點(diǎn)在等待當(dāng)前結(jié)點(diǎn)喚醒。后繼結(jié)點(diǎn)入隊(duì)時(shí),會(huì)將前繼結(jié)點(diǎn)的狀態(tài)更新為 SIGNAL。
  3. CONDITION(-2):表示結(jié)點(diǎn)等待在 Condition 上,當(dāng)其他線(xiàn)程調(diào)用了 Condition 的 signal() 方法后,CONDITION狀態(tài)的結(jié)點(diǎn)將從等待隊(duì)列轉(zhuǎn)移到同步隊(duì)列中,等待獲取同步鎖。
  4. PROPAGATE(-3):共享模式下,前繼結(jié)點(diǎn)不僅會(huì)喚醒其后繼結(jié)點(diǎn),同時(shí)也可能會(huì)喚醒后繼的后繼結(jié)點(diǎn)。
  5. 0:新結(jié)點(diǎn)入隊(duì)時(shí)的默認(rèn)狀態(tài)。

如果前驅(qū)節(jié)點(diǎn)是 SIGNAL 就會(huì)返回 true 表示可以斷開(kāi),如果前驅(qū)節(jié)點(diǎn)的狀態(tài)大于 0 (此時(shí)為什么不用 ws == Node.CANCELLED ) 呢?因?yàn)?ws 大于 0 的條件只有 CANCELLED 狀態(tài)了。然后就是一系列的查找遍歷操作直到前驅(qū)節(jié)點(diǎn)的 waitStatus > 0。如果 ws <= 0 ,而且還不是 SIGNAL 狀態(tài)的話(huà),就會(huì)使用 CAS 替換前驅(qū)節(jié)點(diǎn)的 ws 為 SIGNAL 狀態(tài)。

如果檢查判斷是中斷狀態(tài)的話(huà),就會(huì)返回 false。

  1. private final boolean parkAndCheckInterrupt() { 
  2.   LockSupport.park(this); 
  3.   return Thread.interrupted(); 

這個(gè)方法使用 LockSupport.park 斷開(kāi)連接,然后返回線(xiàn)程是否中斷的標(biāo)志。

  • cancelAcquire() 用于取消等待隊(duì)列,如果等待過(guò)程中沒(méi)有成功獲取資源(如timeout,或者可中斷的情況下被中斷了),那么取消結(jié)點(diǎn)在隊(duì)列中的等待。
  1. private void cancelAcquire(Node node) { 
  2.   if (node == null
  3.     return
  4.  
  5.   node.thread = null
  6.  
  7.   Node pred = node.prev; 
  8.   while (pred.waitStatus > 0) 
  9.     node.prev = pred = pred.prev; 
  10.  
  11.   Node predNext = pred.next
  12.  
  13.   node.waitStatus = Node.CANCELLED; 
  14.  
  15.   if (node == tail && compareAndSetTail(node, pred)) { 
  16.     compareAndSetNext(pred, predNext, null); 
  17.   } else { 
  18.     int ws; 
  19.     if (pred != head && 
  20.         ((ws = pred.waitStatus) == Node.SIGNAL || 
  21.          (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && 
  22.         pred.thread != null) { 
  23.       Node next = node.next
  24.       if (next != null && next.waitStatus <= 0) 
  25.         compareAndSetNext(pred, predNext, next); 
  26.     } else { 
  27.       unparkSuccessor(node); 
  28.     } 
  29.     node.next = node; // help GC 
  30.   } 

所以,對(duì) CountDownLatch 的 await 調(diào)用大致會(huì)有如下的調(diào)用過(guò)程。

一個(gè)和 await 重載的方法是 await(long timeout, TimeUnit unit),這個(gè)方法和 await 最主要的區(qū)別就是這個(gè)方法能夠可以等待計(jì)數(shù)器一段時(shí)間再執(zhí)行后續(xù)操作。

countDown 方法

countDown 是和 await 同等重要的方法,countDown 用于減少計(jì)數(shù)器的數(shù)量,如果計(jì)數(shù)減為 0 的話(huà),就會(huì)釋放所有的線(xiàn)程。

  1. public void countDown() { 
  2.   sync.releaseShared(1); 

這個(gè)方法會(huì)調(diào)用 releaseShared 方法,此方法用于共享模式下的釋放操作,首先會(huì)判斷是否能夠進(jìn)行釋放,判斷的方法就是 CountDownLatch 內(nèi)部類(lèi) Sync 的 tryReleaseShared 方法

  1. public final boolean releaseShared(int arg) { 
  2.   if (tryReleaseShared(arg)) { 
  3.     doReleaseShared(); 
  4.     return true
  5.   } 
  6.   return false
  7.  
  8. // ---- CountDownLatch ---- 
  9.  
  10. protected boolean tryReleaseShared(int releases) { 
  11.   for (;;) { 
  12.     int c = getState(); 
  13.     if (c == 0) 
  14.       return false
  15.     int nextc = c-1; 
  16.     if (compareAndSetState(c, nextc)) 
  17.       return nextc == 0; 
  18.   } 

tryReleaseShared 會(huì)進(jìn)行 for 循環(huán)判斷線(xiàn)程狀態(tài)值,使用 CAS 不斷嘗試進(jìn)行替換。

如果能夠釋放,就會(huì)調(diào)用 doReleaseShared 方法

  1. private void doReleaseShared() { 
  2.   for (;;) { 
  3.     Node h = head; 
  4.     if (h != null && h != tail) { 
  5.       int ws = h.waitStatus; 
  6.       if (ws == Node.SIGNAL) { 
  7.         if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 
  8.           continue;            // loop to recheck cases 
  9.         unparkSuccessor(h); 
  10.       } 
  11.       else if (ws == 0 && 
  12.                !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 
  13.         continue;                // loop on failed CAS 
  14.     } 
  15.     if (h == head)                   // loop if head changed 
  16.       break; 
  17.   } 

可以看到,doReleaseShared 其實(shí)也是一個(gè)無(wú)限循環(huán)不斷使用 CAS 嘗試替換的操作。

總結(jié)

本文是 CountDownLatch 的基本使用和源碼分析,CountDownLatch 就是一個(gè)基于 AQS 的計(jì)數(shù)器,它內(nèi)部的方法都是圍繞 AQS 框架來(lái)談的,除此之外還有其他比如 ReentrantLock、Semaphore 等都是 AQS 的實(shí)現(xiàn),所以要研究并發(fā)的話(huà),離不開(kāi)對(duì) AQS 的探討。CountDownLatch 的源碼看起來(lái)很少,比較簡(jiǎn)單,但是其內(nèi)部比如 await 方法的調(diào)用鏈路卻很長(zhǎng),也值得花費(fèi)時(shí)間深入研究。

本文轉(zhuǎn)載自微信公眾號(hào)「 Java建設(shè)者」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 Java建設(shè)者公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: Java建設(shè)者
相關(guān)推薦

2023-10-16 08:16:31

Bean接口類(lèi)型

2021-12-29 17:38:17

JavaScripttypeof前端

2024-04-12 12:19:08

語(yǔ)言模型AI

2022-03-24 08:51:48

Redis互聯(lián)網(wǎng)NoSQL

2019-11-06 17:30:57

cookiesessionWeb

2023-09-08 08:20:46

ThreadLoca多線(xiàn)程工具

2021-03-22 10:05:59

netstat命令Linux

2023-09-15 12:00:01

API應(yīng)用程序接口

2020-11-04 07:49:04

Select

2023-02-10 10:56:56

KubernetesLimitsRequests

2024-09-27 08:10:57

2022-08-15 15:39:23

JavaScript面向?qū)ο?/a>數(shù)據(jù)

2021-01-13 05:21:59

參數(shù)

2023-08-24 16:50:45

2021-06-30 08:45:02

內(nèi)存管理面試

2019-11-19 08:00:00

神經(jīng)網(wǎng)絡(luò)AI人工智能

2020-03-18 14:00:47

MySQL分區(qū)數(shù)據(jù)庫(kù)

2023-04-03 15:04:00

RPCPHP語(yǔ)言

2024-06-05 11:43:10

2022-06-07 10:13:22

前端沙箱對(duì)象
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: a级片在线观看 | 日韩午夜| 国产精彩视频在线观看 | 波多野结衣二区 | 欧美电影免费观看 | 国产精品免费一区二区三区四区 | 日韩精品一区二区三区第95 | 国产一区二区a | 日韩中文字幕一区二区 | 久久精彩视频 | 久久久成人免费视频 | 日韩国产在线 | 国产精品五月天 | 国产激情一区二区三区 | 99久久久无码国产精品 | 国产一区精品在线 | 午夜影院污| 亚洲高清在线 | 亚洲精品中文字幕在线观看 | 中文字幕日韩一区 | 青青草精品视频 | 欧美一区免费在线观看 | 亚州精品成人 | 久久国产99 | 亚洲综合色站 | 国产精品久久久久久久久久不蜜臀 | 久久性色 | h在线免费观看 | 97av在线| 日韩欧美手机在线 | 日韩一区欧美一区 | 成人在线精品视频 | 中文字幕免费观看 | 亚洲一区 | 免费av直接看| 韩国精品在线 | 二区在线观看 | 国产一区二区久久 | 天堂av在线影院 | 中文字幕在线第一页 | japanhd美女动 |