最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)系列之CyclicBarrier源碼分析

 更新時(shí)間:2018年03月26日 11:22:34   作者:勞夫子  
這篇文章主要為大家詳細(xì)分析了Java并發(fā)系列之CyclicBarrier源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

現(xiàn)實(shí)生活中我們經(jīng)常會(huì)遇到這樣的情景,在進(jìn)行某個(gè)活動(dòng)前需要等待人全部都齊了才開始。例如吃飯時(shí)要等全家人都上座了才動(dòng)筷子,旅游時(shí)要等全部人都到齊了才出發(fā),比賽時(shí)要等運(yùn)動(dòng)員都上場(chǎng)后才開始。在JUC包中為我們提供了一個(gè)同步工具類能夠很好的模擬這類場(chǎng)景,它就是CyclicBarrier類。利用CyclicBarrier類可以實(shí)現(xiàn)一組線程相互等待,當(dāng)所有線程都到達(dá)某個(gè)屏障點(diǎn)后再進(jìn)行后續(xù)的操作。下圖演示了這一過(guò)程。

在CyclicBarrier類的內(nèi)部有一個(gè)計(jì)數(shù)器,每個(gè)線程在到達(dá)屏障點(diǎn)的時(shí)候都會(huì)調(diào)用await方法將自己阻塞,此時(shí)計(jì)數(shù)器會(huì)減1,當(dāng)計(jì)數(shù)器減為0的時(shí)候所有因調(diào)用await方法而被阻塞的線程將被喚醒。這就是實(shí)現(xiàn)一組線程相互等待的原理,下面我們先看看CyclicBarrier有哪些成員變量。

//同步操作鎖
private final ReentrantLock lock = new ReentrantLock();
//線程攔截器
private final Condition trip = lock.newCondition();
//每次攔截的線程數(shù)
private final int parties;
//換代前執(zhí)行的任務(wù)
private final Runnable barrierCommand;
//表示柵欄的當(dāng)前代
private Generation generation = new Generation();
//計(jì)數(shù)器
private int count;

//靜態(tài)內(nèi)部類Generation
private static class Generation {
  boolean broken = false;
}

上面貼出了CyclicBarrier所有的成員變量,可以看到CyclicBarrier內(nèi)部是通過(guò)條件隊(duì)列trip來(lái)對(duì)線程進(jìn)行阻塞的,并且其內(nèi)部維護(hù)了兩個(gè)int型的變量parties和count,parties表示每次攔截的線程數(shù),該值在構(gòu)造時(shí)進(jìn)行賦值。count是內(nèi)部計(jì)數(shù)器,它的初始值和parties相同,以后隨著每次await方法的調(diào)用而減1,直到減為0就將所有線程喚醒。CyclicBarrier有一個(gè)靜態(tài)內(nèi)部類Generation,該類的對(duì)象代表柵欄的當(dāng)前代,就像玩游戲時(shí)代表的本局游戲,利用它可以實(shí)現(xiàn)循環(huán)等待。barrierCommand表示換代前執(zhí)行的任務(wù),當(dāng)count減為0時(shí)表示本局游戲結(jié)束,需要轉(zhuǎn)到下一局。在轉(zhuǎn)到下一局游戲之前會(huì)將所有阻塞的線程喚醒,在喚醒所有線程之前你可以通過(guò)指定barrierCommand來(lái)執(zhí)行自己的任務(wù)。接下來(lái)我們看看它的構(gòu)造器。

//構(gòu)造器1
public CyclicBarrier(int parties, Runnable barrierAction) {
  if (parties <= 0) throw new IllegalArgumentException();
  this.parties = parties;
  this.count = parties;
  this.barrierCommand = barrierAction;
}

//構(gòu)造器2
public CyclicBarrier(int parties) {
  this(parties, null);
}

CyclicBarrier有兩個(gè)構(gòu)造器,其中構(gòu)造器1是它的核心構(gòu)造器,在這里你可以指定本局游戲的參與者數(shù)量(要攔截的線程數(shù))以及本局結(jié)束時(shí)要執(zhí)行的任務(wù),還可以看到計(jì)數(shù)器count的初始值被設(shè)置為parties。CyclicBarrier類最主要的功能就是使先到達(dá)屏障點(diǎn)的線程阻塞并等待后面的線程,其中它提供了兩種等待的方法,分別是定時(shí)等待和非定時(shí)等待。

//非定時(shí)等待
public int await() throws InterruptedException, BrokenBarrierException {
  try {
    return dowait(false, 0L);
  } catch (TimeoutException toe) {
    throw new Error(toe);
  }
}

//定時(shí)等待
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException {
  return dowait(true, unit.toNanos(timeout));
}

可以看到不管是定時(shí)等待還是非定時(shí)等待,它們都調(diào)用了dowait方法,只不過(guò)是傳入的參數(shù)不同而已。下面我們就來(lái)看看dowait方法都做了些什么。

//核心等待方法
private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException {
  final ReentrantLock lock = this.lock;
  lock.lock();
  try {
    final Generation g = generation;
    //檢查當(dāng)前柵欄是否被打翻
    if (g.broken) {
      throw new BrokenBarrierException();
    }
    //檢查當(dāng)前線程是否被中斷
    if (Thread.interrupted()) {
      //如果當(dāng)前線程被中斷會(huì)做以下三件事
      //1.打翻當(dāng)前柵欄
      //2.喚醒攔截的所有線程
      //3.拋出中斷異常
      breakBarrier();
      throw new InterruptedException();
    }
    //每次都將計(jì)數(shù)器的值減1
    int index = --count;
    //計(jì)數(shù)器的值減為0則需喚醒所有線程并轉(zhuǎn)換到下一代
    if (index == 0) {
      boolean ranAction = false;
      try {
        //喚醒所有線程前先執(zhí)行指定的任務(wù)
        final Runnable command = barrierCommand;
        if (command != null) {
          command.run();
        }
        ranAction = true;
        //喚醒所有線程并轉(zhuǎn)到下一代
        nextGeneration();
        return 0;
      } finally {
        //確保在任務(wù)未成功執(zhí)行時(shí)能將所有線程喚醒
        if (!ranAction) {
          breakBarrier();
        }
      }
    }

    //如果計(jì)數(shù)器不為0則執(zhí)行此循環(huán)
    for (;;) {
      try {
        //根據(jù)傳入的參數(shù)來(lái)決定是定時(shí)等待還是非定時(shí)等待
        if (!timed) {
          trip.await();
        }else if (nanos > 0L) {
          nanos = trip.awaitNanos(nanos);
        }
      } catch (InterruptedException ie) {
        //若當(dāng)前線程在等待期間被中斷則打翻柵欄喚醒其他線程
        if (g == generation && ! g.broken) {
          breakBarrier();
          throw ie;
        } else {
          //若在捕獲中斷異常前已經(jīng)完成在柵欄上的等待, 則直接調(diào)用中斷操作
          Thread.currentThread().interrupt();
        }
      }
      //如果線程因?yàn)榇蚍瓥艡诓僮鞫粏拘褎t拋出異常
      if (g.broken) {
        throw new BrokenBarrierException();
      }
      //如果線程因?yàn)閾Q代操作而被喚醒則返回計(jì)數(shù)器的值
      if (g != generation) {
        return index;
      }
      //如果線程因?yàn)闀r(shí)間到了而被喚醒則打翻柵欄并拋出異常
      if (timed && nanos <= 0L) {
        breakBarrier();
        throw new TimeoutException();
      }
    }
  } finally {
    lock.unlock();
  }
}

上面貼出的代碼中注釋都比較詳細(xì),我們只挑一些重要的來(lái)講??梢钥吹皆赿owait方法中每次都將count減1,減完后立馬進(jìn)行判斷看看是否等于0,如果等于0的話就會(huì)先去執(zhí)行之前指定好的任務(wù),執(zhí)行完之后再調(diào)用nextGeneration方法將柵欄轉(zhuǎn)到下一代,在該方法中會(huì)將所有線程喚醒,將計(jì)數(shù)器的值重新設(shè)為parties,最后會(huì)重新設(shè)置柵欄代次,在執(zhí)行完nextGeneration方法之后就意味著游戲進(jìn)入下一局。如果計(jì)數(shù)器此時(shí)還不等于0的話就進(jìn)入for循環(huán),根據(jù)參數(shù)來(lái)決定是調(diào)用trip.awaitNanos(nanos)還是trip.await()方法,這兩方法對(duì)應(yīng)著定時(shí)和非定時(shí)等待。如果在等待過(guò)程中當(dāng)前線程被中斷就會(huì)執(zhí)行breakBarrier方法,該方法叫做打破柵欄,意味著游戲在中途被掐斷,設(shè)置generation的broken狀態(tài)為true并喚醒所有線程。同時(shí)這也說(shuō)明在等待過(guò)程中有一個(gè)線程被中斷整盤游戲就結(jié)束,所有之前被阻塞的線程都會(huì)被喚醒。線程醒來(lái)后會(huì)執(zhí)行下面三個(gè)判斷,看看是否因?yàn)檎{(diào)用breakBarrier方法而被喚醒,如果是則拋出異常;看看是否是正常的換代操作而被喚醒,如果是則返回計(jì)數(shù)器的值;看看是否因?yàn)槌瑫r(shí)而被喚醒,如果是的話就調(diào)用breakBarrier打破柵欄并拋出異常。這里還需要注意的是,如果其中有一個(gè)線程因?yàn)榈却瑫r(shí)而退出,那么整盤游戲也會(huì)結(jié)束,其他線程都會(huì)被喚醒。下面貼出nextGeneration方法和breakBarrier方法的具體代碼。

//切換柵欄到下一代
private void nextGeneration() {
  //喚醒條件隊(duì)列所有線程
  trip.signalAll();
  //設(shè)置計(jì)數(shù)器的值為需要攔截的線程數(shù)
  count = parties;
  //重新設(shè)置柵欄代次
  generation = new Generation();
}

//打翻當(dāng)前柵欄
private void breakBarrier() {
  //將當(dāng)前柵欄狀態(tài)設(shè)置為打翻
  generation.broken = true;
  //設(shè)置計(jì)數(shù)器的值為需要攔截的線程數(shù)
  count = parties;
  //喚醒所有線程
  trip.signalAll();
}

上面我們已經(jīng)通過(guò)源碼將CyclicBarrier的原理基本都講清楚了,下面我們就通過(guò)一個(gè)賽馬的例子來(lái)深入掌握它的使用。

class Horse implements Runnable {
  
  private static int counter = 0;
  private final int id = counter++;
  private int strides = 0;
  private static Random rand = new Random(47);
  private static CyclicBarrier barrier;
  
  public Horse(CyclicBarrier b) { barrier = b; }
  
  @Override
  public void run() {
    try {
      while(!Thread.interrupted()) {
        synchronized(this) {
          //賽馬每次隨機(jī)跑幾步
          strides += rand.nextInt(3);
        }
        barrier.await();
      }
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
  
  public String tracks() {
    StringBuilder s = new StringBuilder();
    for(int i = 0; i < getStrides(); i++) {
      s.append("*");
    }
    s.append(id);
    return s.toString();
  }
  
  public synchronized int getStrides() { return strides; }
  public String toString() { return "Horse " + id + " "; }
  
}

public class HorseRace implements Runnable {
  
  private static final int FINISH_LINE = 75;
  private static List<Horse> horses = new ArrayList<Horse>();
  private static ExecutorService exec = Executors.newCachedThreadPool();
  
  @Override
  public void run() {
    StringBuilder s = new StringBuilder();
    //打印賽道邊界
    for(int i = 0; i < FINISH_LINE; i++) {
      s.append("=");
    }
    System.out.println(s);
    //打印賽馬軌跡
    for(Horse horse : horses) {
      System.out.println(horse.tracks());
    }
    //判斷是否結(jié)束
    for(Horse horse : horses) {
      if(horse.getStrides() >= FINISH_LINE) {
        System.out.println(horse + "won!");
        exec.shutdownNow();
        return;
      }
    }
    //休息指定時(shí)間再到下一輪
    try {
      TimeUnit.MILLISECONDS.sleep(200);
    } catch(InterruptedException e) {
      System.out.println("barrier-action sleep interrupted");
    }
  }
  
  public static void main(String[] args) {
    CyclicBarrier barrier = new CyclicBarrier(7, new HorseRace());
    for(int i = 0; i < 7; i++) {
      Horse horse = new Horse(barrier);
      horses.add(horse);
      exec.execute(horse);
    }
  }
  
}

該賽馬程序主要是通過(guò)在控制臺(tái)不停的打印各賽馬的當(dāng)前軌跡,以此達(dá)到動(dòng)態(tài)顯示的效果。整場(chǎng)比賽有多個(gè)輪次,每一輪次各個(gè)賽馬都會(huì)隨機(jī)走上幾步然后調(diào)用await方法進(jìn)行等待,當(dāng)所有賽馬走完一輪的時(shí)候?qū)?huì)執(zhí)行任務(wù)將所有賽馬的當(dāng)前軌跡打印到控制臺(tái)上。這樣每一輪下來(lái)各賽馬的軌跡都在不停的增長(zhǎng),當(dāng)其中某個(gè)賽馬的軌跡最先增長(zhǎng)到指定的值的時(shí)候?qū)?huì)結(jié)束整場(chǎng)比賽,該賽馬成為整場(chǎng)比賽的勝利者!程序的運(yùn)行結(jié)果如下:

至此我們難免會(huì)將CyclicBarrier與CountDownLatch進(jìn)行一番比較。這兩個(gè)類都可以實(shí)現(xiàn)一組線程在到達(dá)某個(gè)條件之前進(jìn)行等待,它們內(nèi)部都有一個(gè)計(jì)數(shù)器,當(dāng)計(jì)數(shù)器的值不斷的減為0的時(shí)候所有阻塞的線程將會(huì)被喚醒。有區(qū)別的是CyclicBarrier的計(jì)數(shù)器由自己控制,而CountDownLatch的計(jì)數(shù)器則由使用者來(lái)控制,在CyclicBarrier中線程調(diào)用await方法不僅會(huì)將自己阻塞還會(huì)將計(jì)數(shù)器減1,而在CountDownLatch中線程調(diào)用await方法只是將自己阻塞而不會(huì)減少計(jì)數(shù)器的值。另外,CountDownLatch只能攔截一輪,而CyclicBarrier可以實(shí)現(xiàn)循環(huán)攔截。一般來(lái)說(shuō)用CyclicBarrier可以實(shí)現(xiàn)CountDownLatch的功能,而反之則不能,例如上面的賽馬程序就只能使用CyclicBarrier來(lái)實(shí)現(xiàn)??傊@兩個(gè)類的異同點(diǎn)大致如此,至于何時(shí)使用CyclicBarrier,何時(shí)使用CountDownLatch,還需要讀者自己去拿捏。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Mybatis的動(dòng)態(tài)Sql組合模式詳情

    Mybatis的動(dòng)態(tài)Sql組合模式詳情

    這篇文章主要介紹了Mybatis的動(dòng)態(tài)Sql組合模式詳情,這篇文章從組合模式的角度分析了Mybatis動(dòng)態(tài)sql的部分,SqlNode是組合模式的Component接口,更多相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-08-08
  • idea為java程序添加啟動(dòng)參數(shù)的問(wèn)題解析(program?arguments,vm?arguments,Environment?variable)并在程序中獲取使用

    idea為java程序添加啟動(dòng)參數(shù)的問(wèn)題解析(program?arguments,vm?arguments,Envi

    這篇文章主要介紹了idea為java程序添加啟動(dòng)參數(shù)的問(wèn)題解析(program?arguments,vm?arguments,Environment?variable)并在程序中獲取使用,本文給大家分享問(wèn)題描述及解決方法,需要的朋友可以參考下
    2023-09-09
  • Maven本地打包war包實(shí)現(xiàn)代碼解析

    Maven本地打包war包實(shí)現(xiàn)代碼解析

    這篇文章主要介紹了Maven本地打包war包實(shí)現(xiàn)代碼解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • SpringCloud網(wǎng)關(guān)組件Gateway原理深度解析

    SpringCloud網(wǎng)關(guān)組件Gateway原理深度解析

    Spring Cloud Gateway是Spring Cloud微服務(wù)生態(tài)下的網(wǎng)關(guān)組件,一些基礎(chǔ)的請(qǐng)求預(yù)處理的邏輯可以統(tǒng)一實(shí)現(xiàn)在網(wǎng)關(guān)這一層,這樣業(yè)務(wù)服務(wù)只需要專注于處理業(yè)務(wù)邏輯即可,所以本文就帶大家深度解析網(wǎng)關(guān)組件Gateway,需要的朋友可以參考下
    2023-07-07
  • Java Swagger使用教程

    Java Swagger使用教程

    Swagger是一個(gè)規(guī)范和完整的框架,用于生成、描述、調(diào)用和可視化 Restful 風(fēng)格的 Web 服務(wù)??傮w目標(biāo)是使客戶端和文件系統(tǒng)作為服務(wù)器以同樣的速度來(lái)更新。文件的方法、參數(shù)和模型緊密集成到服務(wù)器端的代碼,允許API來(lái)始終保持同步
    2022-07-07
  • Mybatis中返回Map的實(shí)現(xiàn)

    Mybatis中返回Map的實(shí)現(xiàn)

    這篇文章主要介紹了Mybatis中返回Map的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Spring攔截器中注入Bean失敗解放方案詳解

    Spring攔截器中注入Bean失敗解放方案詳解

    這兩天遇到SpringBoot攔截器中Bean無(wú)法注入問(wèn)題。下面介紹關(guān)于SpringBoot攔截器中Bean無(wú)法注入的問(wèn)題解決方案,感興趣的朋友一起看看吧
    2022-06-06
  • java實(shí)現(xiàn)大文本文件拆分

    java實(shí)現(xiàn)大文本文件拆分

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)大文本文件拆分,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • Java代碼生成器的制作流程詳解

    Java代碼生成器的制作流程詳解

    這篇文章主要介紹了Java代碼生成器的制作流程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java線程安全問(wèn)題小結(jié)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java線程安全問(wèn)題小結(jié)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了Java線程安全問(wèn)題小結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-05-05

最新評(píng)論

久治县| 固阳县| 隆尧县| 甘南县| 安徽省| 梅河口市| 黎川县| 龙南县| 德庆县| 高安市| 寻乌县| 容城县| 宣威市| 宜良县| 霍山县| 彭水| 米脂县| 富源县| 扬中市| 仁怀市| 沙洋县| 东光县| 墨脱县| 封丘县| 达尔| 武宣县| 平顺县| 德令哈市| 长兴县| 镇沅| 淮滨县| 周至县| 抚远县| 镇康县| 宁化县| 尼木县| 西青区| 蓬莱市| 临湘市| 盐源县| 曲周县|