# CyclicBarrier与CountDownLatch

返回:源码 | 返回:并发线程

TIP

  • 他们都是Synchronization aid,我把它翻译成同步辅助器
  • CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。【理解成倒计时锁
    • 每位乘客(线程)上车后,可用座位减1,直到为0,老司机就开始发车了。
  • CyclicBarrier是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。【看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍

CyclicBarrier

创建一个CyclicBarrier就类似创建一个派对活动,而构造函数传递的参数parties就代表此次派对活动的成员数量。
但是只有获取到了邀请函才能参加派对,而这个邀请函就是上面可重入锁ReentrantLock。await()代表着到了派对活动现场,到了活动现场首先要检查你是否获取到了邀请函(lock锁),如果没有获取到邀请函则会被拒之门外,
如果获取到了邀请函则会判断派对成员是否都到齐了(count==0?),
如果没有到齐,则需要等待(调用Condition的await()方法等待),
直到最后一个派对成员达到,派对活动才能开始(调用Condition的signalAll()方法唤醒所有成员)。

/**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and
     * does not perform a predefined action when the barrier is tripped.
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties) {
        this(parties, null);
    }

    /**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and which
     * will execute the given barrier action when the barrier is tripped,
     * performed by the last thread entering the barrier.
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @param barrierAction the command to execute when the barrier is
     *        tripped, or {@code null} if there is no action
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

上述为两个构造函数 第一个构造函数只传递一个参数parties:表示派对活动的成员变量。
第二个构造函数传递两个参数,而第二个参数就是成员变量barrierCommand,这个Runnable在所有派对成员都到达派对现场后触发。

public class CyclicBarrierTest {
 private static CyclicBarrier cb = new CyclicBarrier(5);
 public static void main(String[] args) {
    for (int i = 0; i < 5; i++) {
        int finalI = i;
        new Thread(() -> {
        int count = finalI+1;
        System.out.println("Thread" + count + "准备就绪");
        try {
            cb.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println("Thread"+count+"运行结束");
            }).start();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

上面的demo首先创建一个CyclicBarrier对象,并且传递一个参数5,然后创建了5个线程,在调用CyclicBarrier的await()方法前后分别输出准备就绪和运行结束,从上面的运行结果图可以看出,所有线程运行到await()方法时都会被阻塞,直到调用await()方法的线程达到了5个,似乎所有被阻塞的线程都被唤醒了,继续下面的代码逻辑了。

  • 如果我们把上面的线程个数改成6个,会出现什么结果呢?

每一个线程执行到await()方法后被阻塞,直到有5个线程调用await()后,所有被阻塞的线程才被唤醒继续下面的逻辑。而第6个线程执行到await()后,并没有输出“Thread6运行结束”这句话,而是一直等待下去,此次执行也没有结束。

  • 如果我们把上面的线程个数改成4个,又会出现怎样的结果呢?

4个线程在调用await()方法后都一直阻塞下去,并没有执行下面的逻辑。

1:一组线程相互等待,直到执行await()方法的线程个数等于构造函数CyclicBarrier传递的参数。
2:CyclicBarrier可以循环利用传递的参数,执行await()线程的个数到达传递的参数时,好像又被重置了。