国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - 一篇帶給你CountDownLatch實現原理

一篇帶給你CountDownLatch實現原理

2021-04-08 23:25java小當家 JAVA教程

這篇文章帶給你CountDownLatch實現原理,CountDownLatch是多線程中一個比較重要的概念,它可以使得一個或多個線程等待其他線程執行完畢之后再執行。

一篇帶給你CountDownLatch實現原理

 前言

 

CountDownLatch是多線程中一個比較重要的概念,它可以使得一個或多個線程等待其他線程執行完畢之后再執行。它內部有一個計數器和一個阻塞隊列,每當一個線程調用countDown()方法后,計數器的值減少1。當計數器的值不為0時,調用await()方法的線程將會被加入到阻塞隊列,一直阻塞到計數器的值為0。

常用方法

 

  1. public class CountDownLatch { 
  2.  
  3.     //構造一個值為count的計數器 
  4.     public CountDownLatch(int count); 
  5.  
  6.     //阻塞當前線程直到計數器為0 
  7.     public void await() throws InterruptedException; 
  8.  
  9.     //在單位為unit的timeout時間之內阻塞當前線程 
  10.     public boolean await(long timeout, TimeUnit unit); 
  11.  
  12.     //將計數器的值減1,當計數器的值為0時,阻塞隊列內的線程才可以運行 
  13.     public void countDown();       
  14.  

下面給一個簡單的示例:

  1. package com.yang.testCountDownLatch; 
  2.  
  3. import java.util.concurrent.CountDownLatch; 
  4.  
  5. public class Main { 
  6.     private static final int NUM = 3; 
  7.  
  8.     public static void main(String[] args) throws InterruptedException { 
  9.         CountDownLatch latch = new CountDownLatch(NUM); 
  10.         for (int i = 0; i < NUM; i++) { 
  11.             new Thread(() -> { 
  12.                 try { 
  13.                     Thread.sleep(2000); 
  14.                     System.out.println(Thread.currentThread().getName() + "運行完畢"); 
  15.                 } catch (InterruptedException e) { 
  16.                     e.printStackTrace(); 
  17.                 } finally { 
  18.                     latch.countDown(); 
  19.                 } 
  20.             }).start(); 
  21.         } 
  22.         latch.await(); 
  23.         System.out.println("主線程運行完畢"); 
  24.     } 

輸出如下:

一篇帶給你CountDownLatch實現原理

看得出來,主線程會等到3個子線程執行完畢才會執行。

原理解析

 

類圖

一篇帶給你CountDownLatch實現原理

可以看得出來,CountDownLatch里面有一個繼承AQS的內部類Sync,其實是AQS來支持CountDownLatch的各項操作的。

CountDownLatch(int count)

 

new CountDownLatch(int count)用來創建一個AQS同步隊列,并將計數器的值賦給了AQS的state。

  1. public CountDownLatch(int count) { 
  2.     if (count < 0) throw new IllegalArgumentException("count < 0"); 
  3.     this.sync = new Sync(count); 
  4.  
  5. private static final class Sync extends AbstractQueuedSynchronizer {      
  6.     Sync(int count) { 
  7.         setState(count); 
  8.     } 
  9.  

countDown()

 

countDown()方法會對計數器進行減1的操作,當計數器值為0時,將會喚醒在阻塞隊列中等待的所有線程。其內部調用了Sync的releaseShared(1)方法

  1. public void countDown() { 
  2.      sync.releaseShared(1); 
  3.  } 
  4.  
  5.  public final boolean releaseShared(int arg) { 
  6.      if (tryReleaseShared(arg)) { 
  7.          //此時計數器的值為0,喚醒所有被阻塞的線程 
  8.          doReleaseShared(); 
  9.          return true
  10.      } 
  11.      return false
  12.  } 

tryReleaseShared(arg)內部使用了自旋+CAS操將計數器的值減1,當減為0時,方法返回true,將會調用doReleaseShared()方法。對CAS機制不了解的同學,可以先參考我的另外一篇文章淺探CAS實現原理

  1. protected boolean tryReleaseShared(int releases) { 
  2.       //自旋 
  3.       for (;;) { 
  4.           int c = getState(); 
  5.           if (c == 0) 
  6.               //此時計數器的值已經為0了,其他線程早就執行完畢了,當前線程也已經再執行了,不需要再次喚醒了 
  7.               return false
  8.           int nextc = c-1; 
  9.           //使用CAS機制,將state的值變為state-1 
  10.           if (compareAndSetState(c, nextc)) 
  11.               return nextc == 0; 
  12.       } 
  13.   } 

doReleaseShared()是AQS中的方法,該方法會喚醒隊列中所有被阻塞的線程。

  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.      } 
  18.  } 

這段方法比較難理解,會另外篇幅介紹。這里只要認為該段方法會喚醒所有因調用await()方法而阻塞的線程。

await()

 

當計數器的值不為0時,該方法會將當前線程加入到阻塞隊列中,并把當前線程掛起。

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

同樣是委托內部類Sync,調用其

acquireSharedInterruptibly()方法

  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); 
  7.   } 

接著看Sync內的tryAcquireShared()方法,如果當前計數器的值為0,則返回1,最終將導致await()不會將線程阻塞。如果當前計數器的值不為0,則返回-1。

  1. protected int tryAcquireShared(int acquires) { 
  2.         return (getState() == 0) ? 1 : -1; 
  3.     } 

tryAcquireShared方法返回一個負值時,將會調用AQS中的

doAcquireSharedInterruptibly()方法,將調用await()方法的線程加入到阻塞隊列中,并將此線程掛起。

  1. private void doAcquireSharedInterruptibly(int arg) 
  2.       throws InterruptedException { 
  3.       //將當前線程構造成一個共享模式的節點,并加入到阻塞隊列中 
  4.       final Node node = addWaiter(Node.SHARED); 
  5.       boolean failed = true
  6.       try { 
  7.           for (;;) { 
  8.               final Node p = node.predecessor(); 
  9.               if (p == head) {         
  10.                   int r = tryAcquireShared(arg); 
  11.                   if (r >= 0) { 
  12.                       setHeadAndPropagate(node, r); 
  13.                       p.next = null; // help GC 
  14.                       failed = false
  15.                       return
  16.                   } 
  17.               } 
  18.               if (shouldParkAfterFailedAcquire(p, node) && 
  19.                   parkAndCheckInterrupt()) 
  20.                   throw new InterruptedException(); 
  21.           } 
  22.       } finally { 
  23.           if (failed) 
  24.               cancelAcquire(node); 
  25.       } 
  26.   } 

同樣,以上的代碼位于AQS中,在沒有了解AQS結構的情況下去理解上述代碼,有些困難,關于AQS源碼,會另開篇幅介紹。

使用場景

 

CountDownLatch的使用場景很廣泛,一般用于分頭做某些事,再匯總的情景。例如:

數據報表:當前的微服務架構十分流行,大多數項目都會被拆成若干的子服務,那么報表服務在進行統計時,需要向各個服務抽取數據。此時可以創建與服務數相同的線程數,交由線程池處理,每個線程去對應服務中抽取數據,注意需要在finally語句塊中進行countDown()操作。主線程調用await()阻塞,直到所有數據抽取成功,最后主線程再進行對數據的過濾組裝等,形成直觀的報表。

風險評估:客戶端的一個同步請求查詢用戶的風險等級,服務端收到請求后會請求多個子系統獲取數據,然后使用風險評估規則模型進行風險評估。如果使用單線程去完成這些操作,這個同步請求超時的可能性會很大,因為服務端請求多個子系統是依次排隊的,請求子系統獲取數據的時間是線性累加的。此時可以使用CountDownLatch,讓多個線程并發請求多個子系統,當獲取到多個子系統數據之后,再進行風險評估,這樣請求子系統獲取數據的時間就等于最耗時的那個請求的時間,可以大大減少處理時間。

原文地址:https://www.toutiao.com/i6919384508672295436/?group_id=6919384508672295436

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 国产成人高清在线 | 亚洲色图50p | 亚洲综合激情网 | chinese国产一区二区 | 亚洲国产精品免费 | 久久视频精品 | 国产精品视频 | 日韩国产精品一区二区三区 | 亚洲一区二区免费视频 | 欧美精品1区 | 日韩成人在线观看 | 日韩免费av一区二区 | 中文字幕在线观看 | 久久国产精品一区二区 | 日韩视频在线观看 | 久久久精品视频网站 | 久操成人 | 久久91精品| av在线电影网站 | 国产在线一区二区 | 国产一区二区三区久久 | 亚洲精品一 | 日韩国产在线观看 | 综合久久亚洲 | 久久久久久久久国产 | 久久最新 | 视频一区二区三区在线观看 | 黄在线| 久国产精品视频 | 欧美国产一区二区三区 | 五月天导航| 国产欧美一区二区精品久久 | 国产三级黄色毛片 | 午夜天堂精品久久久久 | 国产成人精品一区二区三区视频 | 亚洲高清在线 | 精品国产视频 | 一本大道av日日躁夜夜躁 | 亚洲午夜精品视频 | 成人亚洲网| 久久精品成人一区二区三区蜜臀 |