一、闭锁(Latch)
闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态。通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过,但是一旦大门打开,所有线程都通过了,那么这个闭锁的状态就失效了,门的状态也就不能变了,只能是打开状态。也就是说闭锁的状态是一次性的,它确保在闭锁打开之前所有特定的活动都需要在闭锁打开之后才能完成。
应用场景:
1、确保某个计算在其需要的所有资源都被初始化之后才继续执行。二元闭锁(包括两个状态)可以用来表示“资源R已经被初始化”,而所有需要R的操作都必须先在这个闭锁上等待;
2、确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
3、等待直到某个操作的所有参与者都就绪在继续执行。(例如:多人游戏中需要所有玩家准备才能开始);
二、CountDownLatch
CountDownLatch是一个同步辅助类,用于允许一个或多个线程等待直到多个线程完成一组操作。CountDownLatch初始化时指定一个计数器count,当某个线程完成操作时可以调用countDown()将计数器count减一,任何线程调用CountDownLatch的await()方法都会阻塞直到count等于0。
如上图:主线程TA初始化了一个count=3的CountDownLatch,同时启动三个线程T1,T2,T3 处理一些业务逻辑,TA调用await()方法进入阻塞直到三个线程完成操作。当T1,T2,T3完成操作时,各自调用countDown()将计数器减一,当count=0时TA才会恢复运行。
注意:CountDownLatch 的计数器Count不能重置。
CountDownLatch是一个灵活的闭锁实现,用于如下情况:允许一个或多个线程等待一个事件集的发生。闭锁的状态包括一个计数器,初始化为一个正数,用来实现需要等待的事件数。CountDown方法对计数器做减操作,表示一个事件已经发生了,而await方法等待计数器达到零,此时所有需要等待的事件已发生。如果计数器入口时值为非零,await会一直阻塞直到计数器为零,或者等待线程中断以及超时.
在完成一组正在其他线程中执行的操作之前,它允许线程一直等待。在一些应用场合中,需要等待某个条件达到要求后才能做后面的事情;这个时候就可以使用CountDownLatch。CountDownLatch最重要的方法是countDown()和await(),前者将计数器减一,后者是等待计数到0,如果没有到达0,就阻塞等待。
CountDownLatch强调的是一个线程(或多个)需要等待另外的n个线程干完某件事情之后才能继续执行。CountDownLatch 的初始阀值一旦设置就只能递减下去,无法重置
调用CountDownLatch对象的await()方法则处于等待状态,调用countDown()方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。
三、应用举例
1、模拟最大并发数的例子
2、有三个工人在为老板干活,这个老板有一个习惯,就是当三个工人把一天的活都干完了的时候,他就来检查所有工人所干的活。记住这个条件:三个工人先全部干完活,老板才检查。所以在这里用Java代码设计两个类,Worker代表工人,Boss代表老板
工人:
package LatchAndCyclicBarrier;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class Work implements Runnable{
private CountDownLatch downLatch;
private String name;
public Work(CountDownLatch downLatch, String name){
this.downLatch = downLatch;
this.name = name;
}
public void run() {
this.doWork();
try{
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
}catch(InterruptedException ie){
}
System.out.println(this.name + "活干完了!");
this.downLatch.countDown();
}
private void doWork(){
System.out.println(this.name + "正在干活!");
}
}老板:
package LatchAndCyclicBarrier;
import java.util.concurrent.CountDownLatch;
public class Boss implements Runnable{
private CountDownLatch downLatch;
public Boss(CountDownLatch downLatch){
this.downLatch = downLatch;
}
public void run() {
System.out.println("老板正在等所有的工人干完活......");
try {
this.downLatch.await();
} catch (InterruptedException e) {
}
System.out.println("工人活都干完了,老板开始检查了!");
}
}
测试代码:
package LatchAndCyclicBarrier;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestLatch {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(3);
Work w1 = new Work(latch,"张三");
Work w2 = new Work(latch,"李四");
Work w3 = new Work(latch,"王二");
Boss boss = new Boss(latch);
executor.execute(w3);
executor.execute(w2);
executor.execute(w1);
executor.execute(boss);
executor.shutdown();
}
}
执行结果:
李四正在干活!
老板正在等所有的工人干完活......
王二正在干活!
张三正在干活!
李四活干完了!
王二活干完了!
张三活干完了!
工人活都干完了,老板开始检查了!
更贴切的例子请参见:
http://www.cnblogs.com/liuling/p/2013-8-20-02.html