<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>

          CyclicBarrier和CountDownLatch的用法與區(qū)別

          共 12198字,需瀏覽 25分鐘

           ·

          2021-08-24 22:33


          來源:blog.csdn.net/zyzzxycj/article/details/90241892

          前言

          CyclicBarrier和CountDownLatch這兩個(gè)工具都是在java.util.concurrent包下,并且平時(shí)很多場(chǎng)景都會(huì)使用到。

          本文將會(huì)對(duì)兩者進(jìn)行分析,記錄他們的用法和區(qū)別。

          CountDownLatch

          CountDownLatch是一個(gè)非常實(shí)用的多線程控制工具類,稱之為“倒計(jì)時(shí)器”,它允許一個(gè)或多個(gè)線程一直等待,直到其他線程的操作執(zhí)行完后再執(zhí)行。

          CountDownLatch是通過一個(gè)計(jì)數(shù)器來實(shí)現(xiàn)的,計(jì)數(shù)器的初始值為線程的數(shù)量。每當(dāng)一個(gè)線程完成了自己的任務(wù)后,計(jì)數(shù)器的值就會(huì)減1。當(dāng)計(jì)數(shù)器值到達(dá)0時(shí),它表示所有的線程已經(jīng)完成了任務(wù),然后在閉鎖上等待的線程就可以恢復(fù)執(zhí)行任務(wù)。

          特點(diǎn)

          只能一次性使用(不能reset);主線程阻塞;某個(gè)線程中斷將永遠(yuǎn)到不了屏障點(diǎn),所有線程都會(huì)一直等待。

          例子

            //創(chuàng)建初始化3個(gè)線程的線程池
              private ExecutorService                    threadPool     = Executors.newFixedThreadPool(3);
              //保存每個(gè)學(xué)生的平均成績(jī)
              private ConcurrentHashMap<String, Integer> map            = new ConcurrentHashMap<>();
              private CountDownLatch                     countDownLatch = new CountDownLatch(3);

              private void count() {
                  for (int i = 0; i < 3; i++) {
                      threadPool.execute(() -> {
                          //計(jì)算每個(gè)學(xué)生的平均成績(jī),代碼略()假設(shè)為60~100的隨機(jī)數(shù)
                          int score = (int) (Math.random() * 40 + 60);
                          try {
                              Thread.sleep(Math.round(Math.random() * 1000));
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          map.put(Thread.currentThread().getName(), score);
                          System.out.println(Thread.currentThread().getName() + "同學(xué)的平均成績(jī)?yōu)? + score);
                          countDownLatch.countDown();
                      });
                  }
                  this.run();
                  threadPool.shutdown();
              }

              @Override
              public void run() {
                  try {
                      countDownLatch.await();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  int result = 0;
                  Set<String> set = map.keySet();
                  for (String s : set) {
                      result += map.get(s);
                  }
                  System.out.println("三人平均成績(jī)?yōu)?" + (result / 3) + "分");
              }

              public static void main(String[] args) throws InterruptedException {
                  long now = System.currentTimeMillis();
                  CyclicBarrier1 cb = new CyclicBarrier1();
                  cb.count();
                  Thread.sleep(100);
                  long end = System.currentTimeMillis();
                  System.out.println(end - now);
              }

          最終輸出結(jié)果:

          其中1194ms證明了會(huì)阻塞主線程。另外,關(guān)注Java知音公眾號(hào),回復(fù)“后端面試”,送你一份面試題寶典!

          CyclicBarrier

          CyclicBarrier 的字面意思是可循環(huán)使用(Cyclic)的屏障(Barrier)。它要做的事情是,讓一組線程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞,直到最后一個(gè)線程到達(dá)屏障時(shí),屏障才會(huì)開門,所有被屏障攔截的線程才會(huì)繼續(xù)干活。

          這個(gè)屏障之所以用循環(huán)修飾,是因?yàn)樵谒械木€程釋放彼此之后,這個(gè)屏障是可以重新使用的(reset()方法重置屏障點(diǎn)),這一點(diǎn)與CountDownLatch不同。

          CyclicBarrier是一種同步機(jī)制允許一組線程相互等待,等到所有線程都到達(dá)一個(gè)屏障點(diǎn)才退出await方法,它沒有直接實(shí)現(xiàn)AQS而是借助ReentrantLock來實(shí)現(xiàn)的同步機(jī)制。它是可循環(huán)使用的,而CountDownLatch是一次性的,另外它體現(xiàn)的語義也跟CountDownLatch不同,CountDownLatch減少計(jì)數(shù)到達(dá)條件采用的是release方式,而CyclicBarrier走向屏障點(diǎn)(await)采用的是Acquire方式,Acquire是會(huì)阻塞的,這也實(shí)現(xiàn)了CyclicBarrier的另外一個(gè)特點(diǎn),只要有一個(gè)線程中斷那么屏障點(diǎn)就被打破,所有線程都將被喚醒(CyclicBarrier自己負(fù)責(zé)這部分實(shí)現(xiàn),不是由AQS調(diào)度的),這樣也避免了因?yàn)橐粋€(gè)線程中斷引起永遠(yuǎn)不能到達(dá)屏障點(diǎn)而導(dǎo)致其他線程一直等待。屏障點(diǎn)被打破的CyclicBarrier將不可再使用(會(huì)拋出BrokenBarrierException)除非執(zhí)行reset操作。

          構(gòu)造函數(shù)

          CyclicBarrier有兩個(gè)構(gòu)造函數(shù):

          • CyclicBarrier(int parties)int類型的參數(shù)表示有幾個(gè)線程來參與這個(gè)屏障攔截,(拿上面的例子,即有幾個(gè)人跟團(tuán)旅游);
          • CyclicBarrier(int parties,Runnable barrierAction)當(dāng)所有線程到達(dá)一個(gè)屏障點(diǎn)時(shí),優(yōu)先執(zhí)行barrierAction這個(gè)線程。

          最重要的一個(gè)方法:

          • await():每個(gè)線程調(diào)用await(),表示我已經(jīng)到達(dá)屏障點(diǎn),然后當(dāng)前線程被阻塞。

          例子

           //創(chuàng)建初始化3個(gè)線程的線程池
              private ExecutorService                    threadPool     = Executors.newFixedThreadPool(3);
              //創(chuàng)建3個(gè)CyclicBarrier對(duì)象,執(zhí)行完后執(zhí)行當(dāng)前類的run方法
              private CyclicBarrier                      cb             = new CyclicBarrier(3this);
              //保存每個(gè)學(xué)生的平均成績(jī)
              private ConcurrentHashMap<String, Integer> map            = new ConcurrentHashMap<>();

              private void count() {
                  for (int i = 0; i < 3; i++) {
                      threadPool.execute(() -> {
                          //計(jì)算每個(gè)學(xué)生的平均成績(jī),代碼略()假設(shè)為60~100的隨機(jī)數(shù)
                          int score = (int) (Math.random() * 40 + 60);
                          try {
                              Thread.sleep(Math.round(Math.random() * 1000));
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          map.put(Thread.currentThread().getName(), score);
                          System.out.println(Thread.currentThread().getName() + "同學(xué)的平均成績(jī)?yōu)? + score);
                          try {
                              //執(zhí)行完運(yùn)行await(),等待所有學(xué)生平均成績(jī)都計(jì)算完畢
                              cb.await();
                          } catch (InterruptedException | BrokenBarrierException e) {
                              e.printStackTrace();
                          }
                      });
                  }
                  threadPool.shutdown();
              }

              @Override
              public void run() {
                  int result = 0;
                  Set<String> set = map.keySet();
                  for (String s : set) {
                      result += map.get(s);
                  }
                  System.out.println("三人平均成績(jī)?yōu)?" + (result / 3) + "分");
              }

              public static void main(String[] args) throws InterruptedException {
                  long now = System.currentTimeMillis();
                  CyclicBarrier1 cb = new CyclicBarrier1();
                  cb.count();
                  Thread.sleep(100);
                  long end = System.currentTimeMillis();
                  System.out.println(end - now);
              }

          最終輸出結(jié)果:

          顯然沒有阻塞主線程。

          兩者區(qū)別

          • CountDownLatch的計(jì)數(shù)器只能使用一次。而CyclicBarrier的計(jì)數(shù)器可以使用reset()方法重置。所以CyclicBarrier能處理更為復(fù)雜的業(yè)務(wù)場(chǎng)景,比如如果計(jì)算發(fā)生錯(cuò)誤,可以重置計(jì)數(shù)器,并讓線程們重新執(zhí)行一次。
          • CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線程數(shù)量。isBroken方法用來知道阻塞的線程是否被中斷。比如以下代碼執(zhí)行完之后會(huì)返回true。
          • CountDownLatch會(huì)阻塞主線程,CyclicBarrier不會(huì)阻塞主線程,只會(huì)阻塞子線程。
          • 某線程中斷CyclicBarrier會(huì)拋出異常,避免了所有線程無限等待。

          我們來從jdk作者設(shè)計(jì)的目的來看,javadoc是這么描述它們的:

          CountDownLatch: 
          A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
          CyclicBarrier:
          A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

          從javadoc的描述可以得出:

          • CountDownLatch:一個(gè)或者多個(gè)線程,等待其他多個(gè)線程完成某件事情之后才能執(zhí)行;
          • CyclicBarrier:多個(gè)線程互相等待,直到到達(dá)同一個(gè)同步點(diǎn),再繼續(xù)一起執(zhí)行。

          對(duì)于CountDownLatch來說,重點(diǎn)是“一個(gè)線程(多個(gè)線程)等待”,而其他的N個(gè)線程在完成“某件事情”之后,可以終止,也可以等待。而對(duì)于CyclicBarrier,重點(diǎn)是多個(gè)線程,在任意一個(gè)線程沒有完成,所有的線程都必須等待。

          CountDownLatch是計(jì)數(shù)器,線程完成一個(gè)記錄一個(gè),只不過計(jì)數(shù)不是遞增而是遞減,而CyclicBarrier更像是一個(gè)閥門,需要所有線程都到達(dá),閥門才能打開,然后繼續(xù)執(zhí)行。


          END


          順便給大家推薦一個(gè)GitHub項(xiàng)目,這個(gè) GitHub 整理了上千本常用技術(shù)PDF,絕大部分核心的技術(shù)書籍都可以在這里找到,

          GitHub地址:https://github.com/javadevbooks/books

          Gitee地址:https://gitee.com/javadevbooks/books

          電子書已經(jīng)更新好了,你們需要的可以自行下載了,記得點(diǎn)一個(gè)star,持續(xù)更新中..




          瀏覽 43
          點(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>
                  大香蕉黄网 | 日韩18| 日韩性爱中文字幕 | 成年人一级片 | 夜夜操夜夜 |