您的位置:首页 > 编程语言 > Java开发

Java多线程/并发24、Countdownlatch应用以及与CyclicBarrier的区别

2017-05-07 15:17 507 查看

Countdownlatch应用

有时候会有这样的需求:多个线程同时工作,其中几个可以随意的并发执行,但有一个线程需要等其他线程工作结束后,才能运行。举个例子,我们知道的迅雷下载,会同时开启多个线程分块下载一个大文件,每个线程下载固定的一段,最后由另外一个线程校验并拼接这些分段。这种场景可使用CountDownLatch来控制并发的执行顺序。

Countdownlatch 是一个倒计数器锁。调用CountDownLatch对象的await()方法使线程处于等待状态,调用countDown()方法的线程会将计数器减1,当计数到达0时,所有等待线程(可多个,但通常的应用场景中只有一个等待者)开始继续执行。

这里还是用F1举例:

F1赛车每次进站后,车队技师都需要在尽可能短的时间内对赛车做三个工作:加注燃油、更换轮胎、更换刹车片。当然,这三项工作都是同时进行的。只有当这三项工作完成,赛车才能驶出维修站。

public class CountdownlatchDemo {

/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch cDownLatch=new CountDownLatch(3);//初始计数器值为3,对应3个维修组

/*1、赛车进站*/
System.out.println("F1赛车进站,时间:"+Calendar.getInstance().get(Calendar.SECOND));

/*2、三个维修组同时开始对赛车维护*/
List<mechanician> mechanician_team=new ArrayList<mechanician>();
mechanician_team.add(new mechanician("加注燃油", cDownLatch));
mechanician_team.add(new mechanician("更换轮胎", cDownLatch));
mechanician_team.add(new mechanician("更换刹车片", cDownLatch));
for(mechanician mec:mechanician_team){
new Thread(mec).start();
}

/*3、等待技师完成三项工作。实际就是等待cDownLatch计数器变成0*/
cDownLatch.await();

/*4、完成维护,出发*/
System.out.println("F1赛车维修完毕,出发!时间:"+Calendar.getInstance().get(Calendar.SECOND));
}

/*维修技师类*/
static class mechanician implements Runnable{
String work;
CountDownLatch cDownLatch;
public mechanician(String work,CountDownLatch cDownLatch) {
this.work = work;
this.cDownLatch = cDownLatch;
}
@Override
public void run() {
try {
int random=new Random().nextInt(7);
TimeUnit.SECONDS.sleep(random);
System.out.println(Thread.currentThread().getName()+"--- "+work+" 完成,此组耗时:"+random+"秒");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
/*当前技师的任务完成,cDownLatch计算器减1
*通常countDown放在finally里中使用*/
cDownLatch.countDown();
}
}
}
}


运行输出:

F1赛车进站,时间:37
Thread-1--- 更换轮胎 完成,此组耗时:4秒
Thread-2--- 更换刹车片 完成,此组耗时:4秒
Thread-0--- 加注燃油 完成,此组耗时:5秒
F1赛车维修完毕,出发!时间:42


可以看到,赛车会等待所有工作完成后再出发。

如果删除 cDownLatch.await();会怎么样呢?结果是顺序乱了,赛车并没有待机械维修组完成工作就跑了。

F1赛车进站,时间:18
F1赛车维修完毕,出发!时间:18
Thread-1--- 更换轮胎 完成,此组耗时:5秒
Thread-2--- 更换刹车片 完成,此组耗时:6秒
Thread-0--- 加注燃油 完成,此组耗时:6秒


最后聊一下CountDownLatch和CyclicBarrier的区别

虽然两者在大部分情况下,可以代替使用,但他们是有区别的:

CyclicBarrier可以循环使用,而CountDownLatch只能用一次。但这并不是最主要的区别,也并不是设计这两个类的初衷。

它们主要是为了不同应用场景而设计出来的:

CountDownLatch应用场景:主/从任务模式。是 一个或多个线程(主任务), 等待另外N个线程(从任务)完成某个事情之后才能执行。

CyclicBarrier应用场景:队友模式。一组N个线程(N个队友)相互等待,任意一个线程(某个队友)没有完成任务,所有线程都等着。直到这一组所有线程的任务完成,这组中每个线程才能继续往下运行。

这样应该就清楚一点了,对于CountDownLatch来说,重点是那个“一个或多个线程(主任务)”, 是它在等待, 而另外那N的线程在把“某个事情”做完之后可以继续等待,可以终止。而对于CyclicBarrier来说,重点是N个线程,他们之间任何一个没有完成,所有的线程都必须等待。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐