本文所使用的源碼版本是JDK1.7
CyclicBarrier是什麼?
JDK1.7中的介紹如下:
- A synchronization aid that allows a set of threads to all wait for
- each other to reach a common barrier point. CyclicBarriers are
- useful in programs involving a fixed sized party of threads that
- must occasionally wait for each other. The barrier is called
- cyclic because it can be re-used after the waiting threads
- are released.
CyclicBarrier它允許一組線程等待,等待的時間就是執行了await()以後,相當於在await()處建立了一個Barrier,當Barrier被打破(broken),線程纔會繼續運行await方法後面的程序。而該Barrier在broken後可以重用,所以稱它爲循環的屏障點。CyclicBarrier支持一個可選的Runnable命令,在一組線程中的最後一個線程到達屏障點之後(Barrier被broken),該命令會被最後一個到達Barrier前的一個線程所執行。
CyclicBarrier的例子
這裏我使用了F1賽車的發車的例子。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
public class F1 {
public static class Car implements Runnable {
private int carId;
private final CyclicBarrier barrier;
public Car(int carId, CyclicBarrier barrier) {
this.barrier = barrier;
this.carId = carId;
}
@Override
public void run() {
try {
System.out.println(carId+"號車準備發車");
TimeUnit.MILLISECONDS.sleep(2000);
barrier.await();//所有的都在這裡阻塞,直到控制信號完成
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(carId+"號車起步了");
}
}
public static class Controll implements Runnable{
@Override
public void run() {
System.out.println("所有車都準備好了,信號燈倒計時");
for(int i=3;i>=0;i--){
try {
TimeUnit.SECONDS.sleep(1);
System.out.println((i==0? "發車":i));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("執行完控制檯的controller的run()以後,喚醒之前阻塞的方法!");
}
}
public static void main(String[] args) throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(22, new Controll());
System.out.println("馬來西亞大獎賽正賽開始了");
TimeUnit.SECONDS.sleep(3);
for(int i =1;i<23;i++){
new Thread(new Car(i, barrier)).start();;
}
}
}
/*
馬來西亞大獎賽正賽開始了
1號車準備發車
2號車準備發車
3號車準備發車
4號車準備發車
......
22號車準備發車
所有車都準備好了,信號燈倒計時
3
2
1
發車
執行完控制檯的controller的run()以後,喚醒之前阻塞的方法!
5號車起步了
4號車起步了
2號車起步了
......
22號車起步了
1號車起步了
*/
可以知道,所有的賽車(線程)在執行完await()以後開始在Barrier前面等待,當所有的線程都到達了Barrier前以後,會執行Runnable類的Commond(這裏就是信號燈),當Commond執行完run()以後,會打破Barrier,使得等待着的線程繼續執行剩下的任務。
CyclicBarrier源碼
首先我們先來看一下CyclicBarrier裏面有什麼
public class CyclicBarrier {
/**內部域broken用來表示當前的屏障是否被打破了*/
private static class Generation {
boolean broken = false;
}
/** lock用於保護屏障入口的鎖 */
private final ReentrantLock lock = new ReentrantLock();
/** 用來喚醒阻塞的線程的條件 */
private final Condition trip = lock.newCondition();
/** 線程的數量 */
private final int parties;
/* 所有線程到達Barrier後執行的任務 */
private final Runnable barrierCommand;
/** 當前的柵欄 */
private Generation generation = new Generation();
/**實際中仍在等待的線程數*/
private int count;
}
然後我們來看一下核心方法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();
}
//count減1,表示還未到達屏障點的線程數量
int index = --count;
//如果所有線程都到了屏障點,那麼就由最後一個到達的線程執行Commond
if (index == 0) { // tripped
//表示是否執行了Commond
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//屏障被打破了,那就換一塊屏障,用來進行下一次的攔截
nextGeneration();
return 0;
} finally {
//如果沒有執行Commond,也就是發生了意外
if (!ranAction)
breakBarrier();
}
}
// 線程一直做for循環,直到發生中斷或者超市或者broken
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 {
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();
}
}
breakBarrier()和nextGeneration()
//表示順利執行了Commond,換一下屏障
private void nextGeneration() {
// 讓阻塞的線程繼續執行下去
trip.signalAll();
// 重置count
count = parties;
//換一塊屏障
generation = new Generation();
}
//表示Commond執行出現了意外,修改broken的值,然後繼續執行被屏障攔截的線程
private void breakBarrier() {
//設置broken 爲true
generation.broken = true;
// 重置count
count = parties;
// 讓阻塞的線程繼續執行下去
trip.signalAll();
}
reset()方法
//就是爲了重置屏障
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}