CountDownLatch与CyclicBarrier
java.util.concurrent.CountDownLatch extends Object
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的计数(利用构造函数)初始化CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。等到CountDownLatch的计数为0之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,则考虑使用 CyclicBarrier。
CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。
CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,它只是阻止任何线程继续通过一个 await,及await后面的代码,之后等到CountDownLatch计数为0时才会继续执行。
构造方法:
CountDownLatch(int count) 构造一个用给定计数(count)初始化的 CountDownLatch。
主要方法:
void | await() 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。 |
boolean | await(long timeout, TimeUnit unit) 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。 |
void | countDown() 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。 |
long | getCount() 返回当前计数。 |
String | toString() 返回标识此锁存器及其状态的字符串。 |
java.util.concurrent.CyclicBarrier extends Object
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点,在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。只有当所有参与进来的进程都到达屏障点,他们才能继续往下运行。
构造方法:
CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,它将在给定数量(parties)的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。参数 |
CyclicBarrier(int parties, Runnable barrierAction) 创建一个新的 CyclicBarrier,它将在给定数量(parties)的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。也就是,当所有参与的线程都执行了await方法后,barrierAction中的run过程,以及所有线程await后面的代码才会执行。 |
主要方法:
int | await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 |
int | await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。 |
int | getNumberWaiting() 返回当前在屏障处等待的参与者数目。 |
int | getParties() 返回要求启动此 barrier 的参与者数目。 |
boolean | isBroken() 查询此屏障是否处于损坏状态。 |
void | reset() 将屏障重置为其初始状态。 |
实例1:每个参与的线程Runner在打印出“start runner:”后,必须等待所有的线程都打印出这句话。之后执行BarrierAction,以及继续执行await后面的代码。
public class Test {
class Runner implements Runnable {
int i;
CyclicBarrier cb;
Runner(int i, CyclicBarrier cb) {
this.i = i;
this.cb = cb;
}
@Override
public void run() {
System.out.println("start runner: " + i);
try {
cb.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("end runner: " + i);
}
}
class BarrierAction implements Runnable {
@Override
public void run() {
System.out.println("BarrierAction");
}
}
public static void main(String[] args) {
Test test = new Test();
BarrierAction ba = test.new BarrierAction();
CyclicBarrier cb = new CyclicBarrier(5, ba);
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
es.execute(test.new Runner(i, cb));
}
}
}
运行结果:
start runner: 0
start runner: 2
start runner: 1
start runner: 3
start runner: 4
BarrierAction
end runner: 4
end runner: 2
end runner: 3
end runner: 0
end runner: 1
实例2:CyclicBarrier和CountDownLatch综合应用
模拟赛跑,当所有Runner都准备好后才能统一起跑。每个人跑步的速度(speed)都不一样,要求当所有人跑完length长度之后,统计出最后一个到达终点的人的时间。
public class Runner implements Runnable {
CyclicBarrier cb;
String name; // 选手名称
int length; // 跑步长度
int speed; // 跑步速度
CountDownLatch cd;
public Runner(CountDownLatch cd, CyclicBarrier cb, String name, int speed,
int length) {
this.cb = cb;
this.name = name;
this.speed = speed;
this.length = length;
this.cd = cd;
}
@Override
public void run() {
System.out.println(name + "准备好了!");
try {
cb.await(); // 自己准备好之后等待其他人也准备就绪
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
int kk = 0;
while (length > 0) {
length -= speed;
kk += speed;
System.out.println(name + "跑了" + kk);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
cd.countDown();
System.out.println(name + "到达终点");
}
}
public class BarrierAction implements Runnable {
public void run() {
System.out.println("所有选手起跑了!");
}
}
public class Game {
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(5);
CyclicBarrier cb = new CyclicBarrier(5);
CountDownLatch cd = new CountDownLatch(5);
Long start = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
es.execute(new Runner(cd, cb, "runner" + i, i + 1, 10));
}
cd.await();
// 当所有人都到达终点后,统计处最后一个到达终点的用时。
System.out.println("全部到达终点,用时: " + (System.currentTimeMillis() - start)
* 1.0 / 1000);
es.shutdown();
}
}
运行结果:
runner0准备好了!
runner1准备好了!
runner2准备好了!
runner3准备好了!
runner4准备好了!
所有选手起跑了!
runner4跑了5
runner0跑了1
runner1跑了2
runner2跑了3
runner3跑了4
runner3跑了8
runner2跑了6
runner4跑了10
runner1跑了4
runner0跑了2
runner1跑了6
runner4到达终点
runner2跑了9
runner3跑了12
runner0跑了3
runner0跑了4
runner3到达终点
runner1跑了8
runner2跑了12
runner0跑了5
runner1跑了10
runner2到达终点
runner0跑了6
runner1到达终点
runner0跑了7
runner0跑了8
runner0跑了9
runner0跑了10
runner0到达终点
全部到达终点,用时: 5.002