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
目录
相关文章
|
1月前
|
IDE 小程序 Java
初识Java 2
初识Java
12 0
|
4月前
|
Java
|
6月前
|
存储 SQL 缓存
JAVA问答17
JAVA问答17
54 0
|
7月前
|
Java
Java常见的坑(二)
你猜上述程序输出的是什么? 是 ABC easy as 123 吗? 你执行了输出操作,你才发现输出的是 ABC easy as [C@6e8cf4c6 ,这么一串丑陋的数字是什么鬼? 实际上我们知道字符串与任何数值的相加都会变为字符串,上述事例也不例外, numbers输出其实实际上是调用了Object.toString()方法,让numbers转变为'[c' + '@' + 无符号的十六进制数。
40 0
|
11月前
|
Java 测试技术
1070 结绳(JAVA)
给定一段一段的绳子,你需要把它们串成一条绳。每次串连的时候,是把两段绳子对折,再如下图所示套接在一起。这样得到的绳子又被当成是另一段绳子,可以再次对折去跟另一段绳子串连。每次串连后,原来两段绳子的长度就会减半。
1070 结绳(JAVA)
|
11月前
|
Java
1100 校庆(JAVA)
2019 年浙江大学将要庆祝成立 122 周年。为了准备校庆,校友会收集了所有校友的身份证号。现在需要请你编写程序,根据来参加校庆的所有人士的身份证号,统计来了多少校友。
1100 校庆(JAVA)
|
存储 Java
Java - 9 个小技巧让你的 if else 看起来更优雅(一)
Java - 9 个小技巧让你的 if else 看起来更优雅(一)
298 0
Java - 9 个小技巧让你的 if else 看起来更优雅(一)
|
Java
Java里面的arraycopy总结
Java里面的arraycopy总结
154 0
|
Java
Java StringTokenzier
Java中substring方法可以分解字符串,返回的是原字符串的一个子字符串。如果要讲一个字符串分解为一个一个的单词或者标记,StringTokenizer可以帮你。 1 public static void main(String[] args) { 2 StringTokenizer st = new StringTokenizer("www.
1109 0
相关产品
云迁移中心
相关课程
更多
推荐文章
更多