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
上一篇:DockOne微信分享(八十七):基于Docker的开发云提高资源利用率的实践


下一篇:在GitHub上删除项目后,在Android Studio上传项目依然提示project is already on github