成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

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

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

在網(wǎng)站建設(shè)、網(wǎng)站制作過程中,需要針對客戶的行業(yè)特點(diǎn)、產(chǎn)品特性、目標(biāo)受眾和市場情況進(jìn)行定位分析,以確定網(wǎng)站的風(fēng)格、色彩、版式、交互等方面的設(shè)計(jì)方向。創(chuàng)新互聯(lián)還需要根據(jù)客戶的需求進(jìn)行功能模塊的開發(fā)和設(shè)計(jì),包括內(nèi)容管理、前臺展示、用戶權(quán)限管理、數(shù)據(jù)統(tǒng)計(jì)和安全保護(hù)等功能。

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

在CyclicBarrier類的內(nèi)部有一個(gè)計(jì)數(shù)器,每個(gè)線程在到達(dá)屏障點(diǎn)的時(shí)候都會調(diào)用await方法將自己阻塞,此時(shí)計(jì)數(shù)器會減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)部是通過條件隊(duì)列trip來對線程進(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,該類的對象代表柵欄的當(dāng)前代,就像玩游戲時(shí)代表的本局游戲,利用它可以實(shí)現(xiàn)循環(huán)等待。barrierCommand表示換代前執(zhí)行的任務(wù),當(dāng)count減為0時(shí)表示本局游戲結(jié)束,需要轉(zhuǎn)到下一局。在轉(zhuǎn)到下一局游戲之前會將所有阻塞的線程喚醒,在喚醒所有線程之前你可以通過指定barrierCommand來執(zhí)行自己的任務(wù)。接下來我們看看它的構(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方法,只不過是傳入的參數(shù)不同而已。下面我們就來看看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)前線程被中斷會做以下三件事
      //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ù)來決定是定時(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ì),我們只挑一些重要的來講??梢钥吹皆赿owait方法中每次都將count減1,減完后立馬進(jìn)行判斷看看是否等于0,如果等于0的話就會先去執(zhí)行之前指定好的任務(wù),執(zhí)行完之后再調(diào)用nextGeneration方法將柵欄轉(zhuǎn)到下一代,在該方法中會將所有線程喚醒,將計(jì)數(shù)器的值重新設(shè)為parties,最后會重新設(shè)置柵欄代次,在執(zhí)行完nextGeneration方法之后就意味著游戲進(jìn)入下一局。如果計(jì)數(shù)器此時(shí)還不等于0的話就進(jìn)入for循環(huán),根據(jù)參數(shù)來決定是調(diào)用trip.awaitNanos(nanos)還是trip.await()方法,這兩方法對應(yīng)著定時(shí)和非定時(shí)等待。如果在等待過程中當(dāng)前線程被中斷就會執(zhí)行breakBarrier方法,該方法叫做打破柵欄,意味著游戲在中途被掐斷,設(shè)置generation的broken狀態(tài)為true并喚醒所有線程。同時(shí)這也說明在等待過程中有一個(gè)線程被中斷整盤游戲就結(jié)束,所有之前被阻塞的線程都會被喚醒。線程醒來后會執(zhí)行下面三個(gè)判斷,看看是否因?yàn)檎{(diào)用breakBarrier方法而被喚醒,如果是則拋出異常;看看是否是正常的換代操作而被喚醒,如果是則返回計(jì)數(shù)器的值;看看是否因?yàn)槌瑫r(shí)而被喚醒,如果是的話就調(diào)用breakBarrier打破柵欄并拋出異常。這里還需要注意的是,如果其中有一個(gè)線程因?yàn)榈却瑫r(shí)而退出,那么整盤游戲也會結(jié)束,其他線程都會被喚醒。下面貼出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)通過源碼將CyclicBarrier的原理基本都講清楚了,下面我們就通過一個(gè)賽馬的例子來深入掌握它的使用。

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

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

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

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

分享文章:Java并發(fā)系列之CyclicBarrier源碼分析
文章位置:http://jinyejixie.com/article40/ijjjeo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、定制網(wǎng)站靜態(tài)網(wǎng)站、移動網(wǎng)站建設(shè)、App開發(fā)響應(yīng)式網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營
偏关县| 阳原县| 榆林市| 香港| 寿阳县| 永川市| 农安县| 阳江市| 娄烦县| 阿拉善右旗| 同德县| 辛集市| 石景山区| 澄迈县| 汝州市| 固镇县| 长治市| 迭部县| 柳州市| 额尔古纳市| 木兰县| 泾川县| 景德镇市| 靖宇县| 班戈县| 鸡西市| 天柱县| 昆山市| 黄陵县| 闽侯县| 镇宁| 邵东县| 视频| 清流县| 贵阳市| 房山区| 临邑县| 化州市| 崇信县| 措勤县| 桂平市|