CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。

CyclicBarrier有两个构造方法:

JUC之AQS之CyclicBarrier_构造方法

第一个构造方法只有int参数,这个参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

第二个构造方法多了一个Runnable,用于在线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景。

先看下面这个例子:

package dgb.test.concurrent;

import java.time.LocalDateTime;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* @author Dongguabai
* @date 2018/9/30 11:45
*/
public class CyclicBarrierTest1 implements Runnable {

public static final int COUNT = 5;

private static CyclicBarrier c = new CyclicBarrier(COUNT);

private static CyclicBarrierTest1 cyc = new CyclicBarrierTest1();

public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(COUNT);
for (int i = 0; i < COUNT; i++) {
executor.execute(() -> {
try {
dosth();
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}

public static void dosth() throws BrokenBarrierException, InterruptedException {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 到了!再等等!");
c.await();
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行了!");
}

@Override
public void run() {
try {
dosth();
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
}
}

执行结果:

JUC之AQS之CyclicBarrier_构造方法_02

也可以使用CountDownLatch完成相同的功能:

package dgb.test.concurrent;

import java.time.LocalDateTime;
import java.util.concurrent.*;

/**
* @author Dongguabai
* @date 2018/9/30 11:45
*/
public class CyclicBarrierTest1 implements Runnable {

public static final int COUNT = 5;

// private static CyclicBarrier c = new CyclicBarrier(COUNT);
private static CountDownLatch c = new CountDownLatch(COUNT);

private static CyclicBarrierTest1 cyc = new CyclicBarrierTest1();

public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(COUNT);
for (int i = 0; i < COUNT; i++) {
executor.execute(() -> {
try {
dosth();
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}

public static void dosth() throws BrokenBarrierException, InterruptedException {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 到了!再等等!");
// c.await();
c.countDown();
c.await();
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行了!");
}

@Override
public void run() {
try {
dosth();
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
}
}

将上面示例的CyclicBarrier构造修改一下:

JUC之AQS之CyclicBarrier_构造方法_03

运行结果:

JUC之AQS之CyclicBarrier_java_04

CyclicBarrier和CountDownLatch的区别

CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置。所以CyclicBarrier能处理更为复杂的业务场景。例如,如果计算发生错误,可以重置计数器,并让线程重新执行一次。
CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得Cyclic-Barrier阻塞的线程数量。isBroken()方法用来了解阻塞的线程是否被中断。

package dgb.test.concurrent;

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

public class CyclicBarrierTest3 {
static CyclicBarrier c = new CyclicBarrier(2);

public static void main(String[] args) {
Thread thread = new Thread(()->{
try {
c.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
thread.start();
thread.interrupt();
try {
c.await();
} catch (InterruptedException | BrokenBarrierException e) {
System.out.println(c.isBroken());
e.printStackTrace();
}
}
}

输出结果:

JUC之AQS之CyclicBarrier_java_05

/**
* Exception thrown when a thread tries to wait upon a barrier that is
* in a broken state, or which enters the broken state while the thread
* is waiting.
*
* @see CyclicBarrier
*
* @since 1.5
* @author Doug Lea
*/
public class BrokenBarrierException extends Exception {