类图
源码:
package java.util.concurrent;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class CyclicBarrier {
//使用ReentrantLock可重入独占锁
private final ReentrantLock lock = new ReentrantLock();
//创建一个条件队列
private final Condition trip = lock.newCondition();
//通过构造器传入的参数.表示总的等待线程的数量
private final int parties;
//当屏障正常打开后运行的程序,通过最后一个调用await的线程来执行
private final Runnable barrierCommand;
//当前的Generation。每当屏障失效或者开闸之后都会自动替换掉。从而实现重置的功能
private Generation generation = new Generation();
//实际仍在等待的线程数.当有一个线程到达屏障点,count值就会减一;当一次新的运算开始后,count的值被重置为parties
private int count;
//内部类
private static class Generation {
boolean broken = false;//表示当前的屏障是否被打破
}
//创建一个CyclicBarrier实例,parties指定参与相互等待的线程数
public CyclicBarrier(int parties) {
this(parties, null);
}
//创建一个CyclicBarrier实例,parties指定参与相互等待的线程数
//barrierAction指定当所有线程到达屏障点之后,首先执行的操作,该操作由最后一个进入屏障点的线程执行。
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
//返回参与相互等待的线程数
public int getParties() {
return parties;
}
//该方法被调用时表示当前线程已经到达屏障点,当前线程阻塞进入休眠状态
//直到所有线程都到达屏障点,当前线程才会被唤醒
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe);
}
}
//该方法被调用时表示当前线程已经到达屏障点,当前线程阻塞进入休眠状态
//在timeout指定的超时时间内,等待其他参与线程到达屏障点
//如果超出指定的等待时间,则抛出TimeoutException异常,如果该时间小于等于零,则此方法根本不会等待
public int await(long timeout, TimeUnit unit) throws InterruptedException,BrokenBarrierException,TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
//该方法被调用时表示当前线程已经到达屏障点,当前线程阻塞进入休眠状态
//在timeout指定的超时时间内,等待其他参与线程到达屏障点
//如果超出指定的等待时间,则抛出TimeoutException异常,如果该时间小于等于零,则此方法根本不会等待
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)//如果当前Generation是处于打破状态则传播这个BrokenBarrierExcption
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();//如果当前线程被中断则使得当前generation处于打破状态,重置剩余count,并且唤醒状态变量.这时候其他线程会传播BrokenBarrierException
throw new InterruptedException();
}
int index = --count;//尝试降低当前count
if (index == 0) {
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//当所有参与的线程都到达屏障点,立即去唤醒所有处于休眠状态的线程,恢复执行
nextGeneration();
return 0;
} finally {
if (!ranAction)//如果运行command失败也会导致当前屏障被打破
breakBarrier();
}
}
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();
}
}
//唤醒所有处于休眠状态的线程,恢复执行
//重置count值为parties
//重置中断状态为false
private void nextGeneration() {
trip.signalAll();
count = parties;
generation = new Generation();
}
//唤醒所有处于休眠状态的线程,恢复执行
//重置count值为parties
//重置中断状态为true
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
//判断此屏障是否处于中断状态。
//如果因为构造或最后一次重置而导致中断或超时,从而使一个或多个参与者摆脱此屏障点,或因为异常而导致某个屏障操作失败,则返回true;否则返回false
public boolean isBroken() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return generation.broken;
} finally {
lock.unlock();
}
}
//将屏障重置为其初始状态
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//唤醒所有等待的线程继续执行,并设置屏障中断状态为true
breakBarrier();
//唤醒所有等待的线程继续执行,并设置屏障中断状态为false
nextGeneration();
} finally {
lock.unlock();
}
}
//返回当前在屏障处等待的参与者数目,此方法主要用于调试和断言
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return parties - count;
} finally {
lock.unlock();
}
}
}
类 CyclicBarrier
extends Object
一个同步辅助类:它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。
在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。
因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
CyclicBarrier 支持一个可选的 Runnable
命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
构造方法摘要
CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。 |
CyclicBarrier(int parties, Runnable barrierAction) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。 |
方法摘要
int |
await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 |
int |
await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。 |
int |
getNumberWaiting() 返回当前在屏障处等待的参与者数目。 |
int |
getParties() 返回要求启动此 barrier 的参与者数目。 |
boolean |
isBroken() 查询此屏障是否处于损坏状态。 |
void |
reset() 将屏障重置为其初始状态。 |
CyclicBarrier:将使count等于parties
public CyclicBarrier(int parties,Runnable barrierAction)
创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。
参数:
parties
- 在启动 barrier 前必须调用 await()
的线程数
barrierAction
- 在启动 barrier 时执行的命令;如果不执行任何操作,则该参数为 null
抛出:
IllegalArgumentException
- 如果 parties
小于 1
CyclicBarrier:将使count等于parties
public CyclicBarrier(int parties)
创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。
参数:
parties
- 在启动 barrier 前必须调用 await()
的线程数
抛出:
IllegalArgumentException
- 如果 parties
小于 1
getParties:得到parties的值
public int getParties()
返回要求启动此 barrier 的参与者数目。
返回:
要求启动此 barrier 的参与者数目
await:count减1,若count等于0,则唤醒等待的线程;不等于0,则挂起等待
public int await() throws InterruptedException, BrokenBarrierException
在所有 参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
如果当前线程不是将到达的最后一个线程,出于调度目的,将禁用它,且在发生以下情况之一前,该线程将一直处于休眠状态:
- 最后一个线程到达;或者
- 其他某个线程中断当前线程;或者
- 其他某个线程中断另一个等待线程;或者
- 其他某个线程在等待 barrier 时超时;或者
- 其他某个线程在此 barrier 上调用
reset()
。
如果当前线程:
- 在进入此方法时已经设置了该线程的中断状态;或者
- 在等待时被中断
则抛出 InterruptedException
,并且清除当前线程的已中断状态。
如果在线程处于等待状态时 barrier 被 reset()
,或者在调用 await 时 barrier 被损坏,抑或任意一个线程正处于等待状态,则抛出 BrokenBarrierException
异常。
如果任何线程在等待时被 中断,则其他所有等待线程都将抛出 BrokenBarrierException
异常,并将 barrier 置于损坏状态。
如果当前线程是最后一个将要到达的线程,并且构造方法中提供了一个非空的屏障操作,则在允许其他线程继续运行之前,当前线程将运行该操作。如果在执行屏障操作过程中发生异常,则该异常将传播到当前线程中,并将 barrier 置于损坏状态。
返回:
到达的当前线程的索引,其中,索引 getParties()
- 1 指示将到达的第一个线程,零指示最后一个到达的线程
抛出:
InterruptedException
- 如果当前线程在等待时被中断
BrokenBarrierException
- 如果 另一个 线程在当前线程等待时被中断或超时,或者重置了 barrier,或者在调用 await
时 barrier 被损坏,抑或由于异常而导致屏障操作(如果存在)失败。
await:count减1,若count等于0,则唤醒等待的线程;不等于0,则挂起等待指定时间。在过了指定时间之后,count仍不等于0,则抛出异常。
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
在所有 参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
如果当前线程不是将到达的最后一个线程,出于调度目的,将禁用它,且在发生以下情况之一前,该线程将一直处于休眠状态:
- 最后一个线程到达;
- 超出指定的超时时间;
- 其他某个线程中断当前线程;
- 其他某个线程中断另一个等待线程;
- 其他某个线程在等待 barrier 时超时;
- 其他某个线程在此 barrier 上调用
reset()
。
如果当前线程,在以下情况中的一种时:
- 在进入此方法时已经设置了该线程的中断状态;
- 在等待时被中断
则抛出 InterruptedException
,并且清除当前线程的已中断状态。
如果超出指定的等待时间,则抛出 TimeoutException
异常。如果该时间小于等于零,则此方法根本不会等待。
如果在线程处于等待状态时 barrier 被 reset()
,或者在调用 await 时 barrier 被损坏,抑或任意一个线程正处于等待状态,则抛出 BrokenBarrierException
异常。
如果任何线程在等待时被中断,则其他所有等待线程都将抛出 BrokenBarrierException
,并将屏障置于损坏状态。
如果当前线程是最后一个将要到达的线程,并且构造方法中提供了一个非空的屏障操作,则在允许其他线程继续运行之前,当前线程将运行该操作。如果在执行屏障操作过程中发生异常,则该异常将传播到当前线程中,并将 barrier 置于损坏状态。
参数:
timeout
- 等待 barrier 的时间
unit
- 超时参数的时间单位
返回:
到达的当前线程的索引,其中,索引 getParties()
- 1 指示第一个将要到达的线程,零指示最后一个到达的线程
抛出:
InterruptedException
- 如果当前线程在等待时被中断
TimeoutException
- 如果超出了指定的超时时间
BrokenBarrierException
- 如果 另一个 线程在当前线程等待时被中断或超时,或者重置了 barrier,或者调用 await
时 barrier 被损坏,抑或由于异常而导致屏障操作(如果存在)失败。
isBroken:判断屏障是否损坏
public boolean isBroken()
查询此屏障是否处于损坏状态。
返回:
如果多次调用构造函数或者使用重置函数reset(),在屏障等待的参与者的等待状态会被中断或超时,从而抛出异常。因为异常而导致某个屏障操作失败,则返回 true
;否则返回 false
。
reset:屏障状态重置为true,count等于parties。
public void reset()
将屏障重置为其初始状态。如果所有参与者目前都在屏障处等待,则它们将返回,同时抛出一个 BrokenBarrierException
。
注意:在由于其他原因造成损坏(broken)之后,实行重置可能会变得很复杂;此时需要使用其他方式重新同步线程,并选择其中一个线程来执行重置。与为后续使用创建一个新 barrier 相比,这种方法可能更好一些。
getNumberWaiting:得到当前在屏障处等待的参与数
public int getNumberWaiting()
返回当前在屏障处等待的参与者数目。此方法主要用于调试和断言。
返回:
当前阻塞在 await()
中的参与者数目。
使用实例:
- 1.新建5个线程,这5个线程达到一定的条件时,它们才继续往后运行。
package com.thread;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest1 extends Thread {
private static int SIZE = 5;
private static CyclicBarrier cb;
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " wait for CyclicBarrier.");
// CyclicBarrier的count减1,若count等于0,则唤醒在屏障处等待的所有线程
cb.await();
System.out.println(Thread.currentThread().getName() + " continued.");
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
cb = new CyclicBarrier(SIZE);
// 新建5个任务
for (int i = 0; i < SIZE; i++)
new CyclicBarrierTest1().start();
}
}
运行结果:
Thread-1 wait for CyclicBarrier.
Thread-3 wait for CyclicBarrier.
Thread-0 wait for CyclicBarrier.
Thread-2 wait for CyclicBarrier.
Thread-4 wait for CyclicBarrier.
Thread-4 continued.
Thread-3 continued.
Thread-2 continued.
Thread-1 continued.
Thread-0 continued.
- 2.新建5个线程,当这5个线程达到一定的条件时,执行某项任务。
package com.thread;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest2 extends Thread {
private static int SIZE = 5;
private static CyclicBarrier cb;
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " wait for CyclicBarrier.");
// CyclicBarrier的count减1,若count等于0,则唤醒在屏障处等待的所有线程
cb.await();
System.out.println(Thread.currentThread().getName() + " continued.");
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
cb = new CyclicBarrier(SIZE, new Runnable() {
public void run() {//当其他的线程都已达到barrier,先执行当前任务,再让其他线程继续执行
System.out.println("CyclicBarrier's parties is: " + cb.getParties());
}
});
// 新建5个任务
for (int i = 0; i < SIZE; i++)
new CyclicBarrierTest1().start();
}
}
运行结果:
Thread-0 wait for CyclicBarrier.
Thread-2 wait for CyclicBarrier.
Thread-1 wait for CyclicBarrier.
Thread-4 wait for CyclicBarrier.
Thread-3 wait for CyclicBarrier.
CyclicBarrier's parties is: 5
Thread-3 continued.
Thread-1 continued.
Thread-4 continued.
Thread-0 continued.
Thread-2 continued.