java.util.concurrent包(5)——CountDownLatch使用-阿里云开发者社区

开发者社区> it徐胖子> 正文

java.util.concurrent包(5)——CountDownLatch使用

简介:
+关注继续查看
Java的concurrent包里面的CountDownLatch其实可以被看作一个计数器,只不过这个计数器的操作是原子操作,同时只能有一个线程去操作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。

可以向CountDownLatch对象设置一个初始的数字作为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数器的计数值被其他的线程减为0为止。

CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

主要方法
public CountDownLatch(int count)
public void countDown()
public void await() throws InterruptedException
 
构造方法参数指定了计数的次数
countDown方法,当前线程调用此方法,则计数减一
await方法,调用此方法会一直阻塞当前线程,直到计时器的值为0

例如有三个工人在为老板干活,这个老板有一个习惯,就是当三个工人把一天的活都干完了的时候,他就来检查所有工人所干的活。记住这个条件:三个工人先全部干完活,老板才检查。

public class Boss implements Runnable
{
private final CountDownLatch countDownLatch;
private String name;

public Boss(String name, CountDownLatch countDownLatch)
{
super();
this.name = name;
this.countDownLatch = countDownLatch;
}

public void run()
{
try
{
System.out.println("老板" + name + "正在等工人把活干完");
countDownLatch.await();
System.out.println("工人活都干完了,老板开始检查了!");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

public class Worker implements Runnable
{
private final CountDownLatch countDownLatch;
private String name;

public Worker(String name, CountDownLatch countDownLatch)
{
super();
this.name = name;
this.countDownLatch = countDownLatch;
}

public void run()
{
doWork();
countDownLatch.countDown();
}

private void doWork()
{
try
{
System.out.println(this.name + "正在干活!");
long duration = new Random().nextInt(5) * 1000;
Thread.sleep(duration);
System.out.println(this.name + "活干完啦!");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

public class BossWorkerTest
{
public static void main(String[] args)
{
ExecutorService executor = Executors.newCachedThreadPool();
CountDownLatch countDownLatch = new CountDownLatch(3);

Worker w1 = new Worker("张一", countDownLatch);
Worker w2 = new Worker("张二", countDownLatch);
Worker w3 = new Worker("张三", countDownLatch);
Boss boss = new Boss("王老板", countDownLatch);
executor.execute(w3);
executor.execute(w2);
executor.execute(w1);
executor.execute(boss);
executor.shutdown();
}
}

下面是本地机器上运行的一次结果,每次运行的结果可能与下面不一样,但老板检查永远是在最后面。

张三正在干活!
张二正在干活!
张一正在干活!
张三活干完啦!
老板王老板正在等工人把活干完
张一活干完啦!
张二活干完啦!
工人活都干完了,老板开始检查了!

再看一个例子,该程序用来模拟发送命令与执行命令。
主线程代表教练,新建3个线程代表运动员。
运动员一直等待着教练下达命令。若教练没有下达命令,则运动员们都必须等待。
一旦命令下达,运动员们都去执行自己的任务,教练处于等待状态,运动员们任务执行完毕则报告给教练,教练则结束等待。

public class CountdownLatchTest
{
public static void main(String[] args)
{
// 创建一个线程池
ExecutorService service = Executors.newCachedThreadPool();
// 教练的命令设置为1,教练一下达命令,则countDown变为0,运动员们执行任务
final CountDownLatch cdOrder = new CountDownLatch(1);
// 因为有三个运动员故初始值为3,每个运动员执行任务完毕则countDown一次,当三个都执行完毕变为0则教练停止等待
final CountDownLatch cdAnswer = new CountDownLatch(3);
for (int i = 0; i < 3; i++)
{
Runnable runnable = new Runnable()
{
public void run()
{
try
{
System.out.println("线程" + Thread.currentThread().getName() + "正准备接受命令");
// 运动员们都处于等待命令状态
cdOrder.await();
System.out.println("线程" + Thread.currentThread().getName() + "已接受命令");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "回应命令处理结果");
// 任务执行完毕返回给教练,cdAnswer减1
cdAnswer.countDown();
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try
{
Thread.sleep((long) (Math.random() * 10000));

System.out.println("线程" + Thread.currentThread().getName() + "即将发布命令");
// 发送命令cdOrder减1,处于等待的运动员们停止等待转去执行任务
cdOrder.countDown();
System.out.println("线程" + Thread.currentThread().getName() + "已发送命令,正在等待结果");
// 命令发送后教练处于等待状态,一旦cdAnswer为0时停止等待继续往下执行
cdAnswer.await();
System.out.println("线程" + Thread.currentThread().getName() + "已收到所有响应结果");
}
catch (Exception e)
{
e.printStackTrace();
}
service.shutdown();
}
}

原帖地址:
http://zapldy.iteye.com/blog/746458
http://www.cnblogs.com/liuling/p/2013-8-20-02.html

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
9447 0
在Markdown中使用目录
在markdown中使用目录的正确形式
1577 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
10828 0
java.util.concurrent包(7)——Exchanger使用
Java 并发 API 提供了一种允许2个并发任务间相互交换数据的同步应用。更具体的说,Exchanger类允许在2个线程间定义同步点,当2个线程到达这个点,他们相互交换数据类型,使用第一个线程的数据类型变成第二个的,然后第二个线程的数据类型变成第一个的。示例1 一个人有零食,另一个人有钱,他们两个想等价交换,对好口号在某个地方相见,一个人先到了之后,必须等另一个人带着需要的东西来了之后
1196 0
iphone的系统信息使用[UIDevice currentDevice]
<p style="line-height:28px; margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; color:rgb(51,51,51); font-family:宋体; font-size:16px; border-width:0px; list-style:none"> 获取ipho
1112 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
13113 0
excel中COUNTIF的使用
=(COUNTIF(D9:AH9,"●")+COUNTIF(D7:AH7,"●"))*0.5
1110 0
+关注
it徐胖子
IT徐胖子
733
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载