CountDownLatch
假设有这样一个场景:
两个runner等待起跑, 枪响, 然后观众等待比赛结束. 高声喊出谁是冠军. 同时裁判(主线程)也宣布比赛结束.
class Player implements Runnable{
private CountDownLatch begin;
private CountDownLatch end;
Player(CountDownLatch begin,CountDownLatch end){
this.begin = begin;
this.end = end;
}
public void run() {
try {
begin.await();//在等待召唤
System.out.println(Thread.currentThread().getName() + " arrived !");;
end.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//观众也在等待比赛结束
class Audience implements Runnable{
private CountDownLatch end;
Audience(CountDownLatch end){
this.end = end;
}
public void run() {
try {
end.await();
System.out.println(Thread.currentThread().getName() + " aha I verify that xx is the champion!");;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class runner {
public static void main(String[] args) {
CountDownLatch begin = new CountDownLatch(1);
CountDownLatch end = new CountDownLatch(2);
for(int i=0; i<2; i++){
Thread thread = new Thread(new Player(begin,end));
thread.start();
}
Thread thread3 = new Thread(new Audience(end),"Audience");
thread3.start();
try{
System.out.println("the race begin");
//唤醒其他阻塞线程. 可唤醒一个. 有两个runner在竞争.
begin.countDown();
//我们在构造函数中添加了参数2,就需要调用 2 次 countDown()
//才能将 end.await() 阻塞的线程唤醒。
//当两个runner都调用了countDown() 此时 比赛可以结束了. end.await可以继续执行了.
end.await();
System.out.println("the race end");
}catch(Exception e){
e.printStackTrace();
}
}
}
输出:
the race begin
Thread-1 arrived !
Thread-0 arrived !
Audience aha I verify that xx is the champion!
the race end
CyclicBarrier
假设有这样一个场景:
y = a(x) + b(x) + c(x) + d(x) + e(x)
想知道y, 必须同时知道a b c d e 的计算结果. 那为了节省时间. 就同时开5个线程去计算 a b c d e,
然后最后计算出结果的人, 把5个结果相加, 就得出y的值了
class TaskThread extends Thread {
CyclicBarrier barrier;
public TaskThread(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
System.out.println(getName() + " 计算: 第一步参数(需要1s)");
Thread.sleep(1000);
System.out.println(getName() + " 完成: 第一步参数");
barrier.await();
System.out.println(getName() + " 开始: 第二步计算(需要2s)");
Thread.sleep(2000);
System.out.println(getName() + " 完成: 第二步参数");
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class CyclicBarrierDemo {
public static void main(String[] args) {
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 计算汇总(最后完成的人汇总!)");
}
});
for(int i = 0; i < threadNum; i++) {
new TaskThread(barrier).start();
}
}
}
结果
Thread-0 计算: 第一步参数(需要1s)
Thread-2 计算: 第一步参数(需要1s)
Thread-1 计算: 第一步参数(需要1s)
Thread-3 计算: 第一步参数(需要1s)
Thread-4 计算: 第一步参数(需要1s)
Thread-4 完成: 第一步参数
Thread-2 完成: 第一步参数
Thread-0 完成: 第一步参数
Thread-3 完成: 第一步参数
Thread-1 完成: 第一步参数
Thread-1 计算汇总(最后完成的人汇总!)
Thread-1 开始: 第二步计算(需要2s)
Thread-4 开始: 第二步计算(需要2s)
Thread-0 开始: 第二步计算(需要2s)
Thread-2 开始: 第二步计算(需要2s)
Thread-3 开始: 第二步计算(需要2s)
Thread-4 完成: 第二步参数
Thread-3 完成: 第二步参数
Thread-2 完成: 第二步参数
Thread-1 完成: 第二步参数
Thread-0 完成: 第二步参数
Thread-0 计算汇总(最后完成的人汇总!)
总结
以上是两个应用场景
我们来比较一下CyclicBarrier和CountDownLatch
他们有什么不同:
1.CountDownLatch可以是一个线程, 或者多个线程在等待计数为0, 然后同时触发. 比如:观众和主线程在一起等赛跑结束.
2.CyclicBarrier功能与CountDownLatch类似. 但是他可以循环使用, 比如:运算一次之后, 还可以重新计算.
3. CyclicBarrier, 所有线程的职责一致. 都是类似减少计数, 达到要求选一个人执行CyclicBarrier预先设定好的任务.
4. CountDownLatch中, 有条件线程, 也有结果线程. 条件线程执行然后减少计数(调用countDown).
结果线程在条件符合时(计数为0), 将执行自己的逻辑.