Java multi-threaded programming-barrier CyclicBarrier example

Preface

This article is based on my personal understanding of Chapter 5 of "Java Multi-Threaded Programming Practical Guide - Core Chapter". The source code is excerpted from the author's source code. The source code will add your own understanding. The reading notes are currently being updated by the author as follows, "Java Multi-Threaded Programming Practical Guide - Core Chapter", "How Tomcat Works", and then "spring source code" Interpretation.

FenceCyclicBarrier


Sometimes multiple threads may need to wait for each other to execute to a certain place in the code (rendezvous point), so that these threads can continue to execute. This kind of waiting is similar to the situation when everyone makes an appointment to go mountain climbing: everyone reaches the meeting point at the agreed time, and the person who arrives first must wait for the others who have not arrived at the meeting point. Only after all participants have arrived can everyone be triggered to go mountain climbing. Java.util.concurrent.CyclicBarrier, this class can be used to implement this kind of waiting.

The thread that uses CyclicBarrier to wait is called a participant, and the participant only needs to execute CyclicBarrier.await() to wait. Although from the perspective of application code, the participants execute CyclicBarrier.await() concurrently, CyclicBarrier maintains an explicit lock internally and can always distinguish the last thread that executed CyclicBarrier.await(). All parties except the last thread are suspended and in WAITING state. When the last thread executes await, it will wake up other participants, but the last thread itself will not pause.

Different from CountDownLatch (for CountDownLatch, please refer to the previous blog), when all participants wake up, any thread that executes await again will be suspended until the last thread of these threads executes await.

Example

The example simulates soldiers participating in target shooting training. All participating soldiers are divided into several groups of ranks. Each group is called a row. The number of soldiers in a row is equal to the number of targets. Only one row of soldiers can shoot at a time. A row of soldiers must shoot at the same time. The soldiers who have finished shooting must wait for other soldiers in the same row to finish shooting before the whole row can evacuate the shooting point. Then they take turns shooting row after row.

Basic explanation of code

1. The main function ShootPractice is instantiated, the input parameters are N (number of soldiers in each row = number of targets), linecount (number of rows), lasting (maximum duration), and all are instantiated according to the number of soldiers (N*linecount) Soldiers are stored in rank.

2. Two CycliBarriers are instantiated, shiftBarrier and startBarrier. startBarrier ensures that soldiers can shoot at the same time. It can be understood as shown in the above figure whether the soldiers are ready. In the figure, soldiers one and four are standing at the shooting location. Ready state, need to wait for soldiers two and three.

The shiftBarrier ensures that all soldiers evacuate the shooting position at the same time after shooting. This is because some soldiers have completed shooting and some have not. As shown in the figure, soldiers one and two have finished shooting, and soldiers three and four are still shooting, so soldiers One and two need to wait for three and four.

3. So in the thread run function, there are two waits before and after the fire function, one is waiting for everyone to be ready, and the other is waiting for everyone to complete the shooting.

code

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class ShootPractice {
  // 参与打靶训练的全部士兵
  final Soldier[][] rank;
  // 靶的个数,即每排中士兵的个数
  final int N;
  // 打靶持续时间(单位:秒)
  final int lasting;
  // 标识是否继续打靶
  volatile boolean done = false;
  // 用来指示进行下一轮打靶的是哪一排的士兵
  volatile int nextLine = 0;
  final CyclicBarrier shiftBarrier;
  final CyclicBarrier startBarrier;

  public ShootPractice(int N, final int lineCount, int lasting) {
    this.N = N;
    this.lasting = lasting;
    this.rank = new Soldier[lineCount][N];
    for (int i = 0; i < lineCount; i++) {
      for (int j = 0; j < N; j++) {
        rank[i][j] = new Soldier(i * N + j);
      }
    }
    shiftBarrier = new CyclicBarrier(N, new Runnable() {
      @Override
      public void run() {
        // 更新下一轮打靶的排
        nextLine = (nextLine + 1) % lineCount;// 语句①
        Debug.info("Next turn is :%d", nextLine);
      }
    });
    // 语句②
    startBarrier = new CyclicBarrier(N);
  }

  public static void main(String[] args) throws InterruptedException {
    ShootPractice sp = new ShootPractice(4, 5, 24);
    sp.start();
  }

  public void start() throws InterruptedException {
    // 创建并启动工作者线程
    Thread[] threads = new Thread[N];
    for (int i = 0; i < N; ++i) {
      threads[i] = new Shooting(i);
      threads[i].start();
    }
    // 指定时间后停止打靶
    Thread.sleep(lasting * 1000);
    stop();
    for (Thread t : threads) {
      t.join();
    }
    Debug.info("Practice finished.");
  }

  public void stop() {
    done = true;
  }

  class Shooting extends Thread {
    final int index;

    public Shooting(int index) {
      this.index = index;
    }

    @Override
    public void run() {
      Soldier soldier;
      try {
        while (!done) {
          soldier = rank[nextLine][index];
          // 一排中的士兵必须同时开始射击
          startBarrier.await();// 语句③
          // 该士兵开始射击
          soldier.fire();
          // 一排中的士兵必须等待该排中的所有其他士兵射击完毕才能够离开射击点
          shiftBarrier.await();// 语句④
        }
      } catch (InterruptedException e) {
        // 什么也不做
      } catch (BrokenBarrierException e) {
        e.printStackTrace();
      }

    }// run方法结束
  }// 类Shooting定义结束

  // 参与打靶训练的士兵
  static class Soldier {
    private final int seqNo;

    public Soldier(int seqNo) {
      this.seqNo = seqNo;
    }

    public void fire() {
      Debug.info(this + " start firing...");
      Tools.randomPause(5000);
      System.out.println(this + " fired.");
    }

    @Override
    public String toString() {
      return "Soldier-" + seqNo;
    }

  }// 类Soldier定义结束
}

For debugging and tools, please refer to the previous blog

references

"Java Multi-Threaded Programming Practice-Core"

Guess you like

Origin blog.csdn.net/u012895183/article/details/133337944