CyclicBarrier 分析

lzyer發表於2021-05-31

簡介

  1. CyclicBarrier 是什麼?
  2. CyclicBarrier 使用
  3. CyclicBarrier 原始碼解析
  4. CyclicBarrier 簡單實現
  5. barrierAction 是由哪個執行緒執行的?

CyclicBarrier 是什麼?

CyclicBarrier 開始於JDK 1.5, 一個同步工具類,允許一組執行緒都等待彼此到達公共屏障點。CyclicBarrier 在程式中非常有用,涉及到固定引數的執行緒數量等待彼此,這個 barrier 被稱為 cyclic 是由於它可以所有的等待執行緒釋放之後,重複使用。
CyclicBarrier 支援一個可選的 Runnable 在每一個屏障點執行一次,在所有參與的執行緒到達之後,但是在執行之前所有的執行緒都釋放了, barrierAction非常有用的對於在任何參與者繼續之前更新共享狀態。

CyclicBarrier 使用

public class Test2 {

  static class A extends Thread {

    private CyclicBarrier cyclicBarrier;

    public A(CyclicBarrier cyclicBarrier, String name) {
      this.cyclicBarrier = cyclicBarrier;
      setName(name);
    }

    @Override
    public void run() {
      try {
        System.out.println(Thread.currentThread().getName() + " 準備完畢");
        cyclicBarrier.await();
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (BrokenBarrierException e) {
        e.printStackTrace();
      }
    }
  }

  static class B extends Thread {

    private CyclicBarrier cyclicBarrier;

    public B(CyclicBarrier cyclicBarrier, String name) {
      this.cyclicBarrier = cyclicBarrier;
      setName(name);
    }

    @Override
    public void run() {
      try {
        System.out.println(Thread.currentThread().getName() + " 準備完畢");
        cyclicBarrier.await();
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (BrokenBarrierException e) {
        e.printStackTrace();
      }
    }
  }

  static class C extends Thread {

    private CyclicBarrier cyclicBarrier;

    public C(CyclicBarrier cyclicBarrier, String name) {
      this.cyclicBarrier = cyclicBarrier;
      setName(name);
    }

    @Override
    public void run() {
      try {
        System.out.println(Thread.currentThread().getName() + " 準備完畢");
        cyclicBarrier.await();
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (BrokenBarrierException e) {
        e.printStackTrace();
      }
    }
  }


  public static void main(String[] args) {
    CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> System.out.println("完成"));
    new A(cyclicBarrier, "A").start();
    new B(cyclicBarrier, "B").start();
    new C(cyclicBarrier, "C").start();
  }
}
/**
output:

A 準備完畢
B 準備完畢
C 準備完畢
完成
*/

CyclicBarrier 原始碼解析

在看原始碼前,我們可以對原始碼的實現進行一些猜想,根據 CyclicBarrier 前面的定義,可以猜想裡面有一個變數來表示參與者的數量,在使用呼叫 await 方法是時候,參與者的數量減一,直到參與者數量為 0,存在 barrierAction,就執行barrierAction,由於可以重複使用,所以在barrierAction 執行對參與者的數量進行恢復。
下面看一下原始碼實現是否於猜想的類似。

構造方法

parties 參與者的數量
barrierAction 最後執行的動作(可選)

public CyclicBarrier(int parties) {
        this(parties, null);
    }

public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

await 方法

 public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;

            if (g.broken)
                throw new BrokenBarrierException();

            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            // 減去一個參與者
            int index = --count;
           // 如果參與者數量為0,判斷barrierAction是否為null,不為null, 將執行run方法,呼叫nextGeneration恢復狀態
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            for (;;) {
                try {
                    if (!timed)
                        trip.await(); // 在屏障點等待
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }

                if (g.broken)
                    throw new BrokenBarrierException();

                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

nextGeneration 方法

private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();  // 喚醒參與者
        // set up next generation
        count = parties; // 恢復參與者的數量
        generation = new Generation(); // 下一代
    }

CyclicBarrier 簡單實現

藉助 AtomicInteger 來簡單實現

public class SimpleBarrier {

  private AtomicInteger count;
  int size;
  private Runnable command;

  public SimpleBarrier(int n) {
    this.count = new AtomicInteger(n);
    this.size = n;
  }

  public SimpleBarrier(int n, Runnable barrierAction) {
    this(n);
    this.command = barrierAction;
  }

  public void await() {
    int position = count.getAndDecrement();
    if (position == 1) {
      command.run();
      count.set(size);
    } else {
      while (count.get() != 0) {
      }
    }
  }
}
public class Test2 {

  static class A extends Thread {

    private SimpleBarrier cyclicBarrier;

    public A(SimpleBarrier cyclicBarrier, String name) {
      this.cyclicBarrier = cyclicBarrier;
      setName(name);
    }

    @Override
    public void run() {
      System.out.println(Thread.currentThread().getName() + " 準備完畢");
      cyclicBarrier.await();
    }
  }

  static class B extends Thread {

    private SimpleBarrier cyclicBarrier;

    public B(SimpleBarrier cyclicBarrier, String name) {
      this.cyclicBarrier = cyclicBarrier;
      setName(name);
    }

    @Override
    public void run() {
      System.out.println(Thread.currentThread().getName() + " 準備完畢");
      cyclicBarrier.await();
    }
  }

  static class C extends Thread {

    private SimpleBarrier cyclicBarrier;

    public C(SimpleBarrier cyclicBarrier, String name) {
      this.cyclicBarrier = cyclicBarrier;
      setName(name);
    }

    @Override
    public void run() {
      System.out.println(Thread.currentThread().getName() + " 準備完畢");
      cyclicBarrier.await();
    }
  }


  public static void main(String[] args) {
    SimpleBarrier cyclicBarrier = new SimpleBarrier(3, () -> System.out.println("完成"));
    new A(cyclicBarrier, "A").start();
    new B(cyclicBarrier, "B").start();
    new C(cyclicBarrier, "C").start();
  }
}
/**
output:
A 準備完畢
B 準備完畢
C 準備完畢
完成
*/

barrierAction 是由哪個執行緒執行的?

最後一個執行緒來執行。

相關文章