Java并发工具类 - CountDownLatch
1、简介
CountDownLatch能够使一个或多个线程等待其他线程完成各自的工作后再执行;CountDownLatch是JDK 5+里面闭锁的一个实现。
闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态。通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过,但是一旦大门打开,所有线程都通过了,那么这个闭锁的状态就失效了,门的状态也就不能变了,只能是打开状态。也就是说闭锁的状态是一次性的,它确保在闭锁打开之前所有特定的活动都需要在闭锁打开之后才能完成。
与CountDownLatch第一次交互是主线程等待其它的线程,主线程必须在启动其它线程后立即调用await方法,这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。
其他的N个线程必须引用闭锁对象,因为他们需要通知CountDownLatch对象,他们已经完成了各自的任务,这种机制就是通过countDown()方法来完成的。每调用一次这个方法,在构造函数中初始化的count值就减1,所以当N个线程都调用了这个方法count的值等于0,然后主线程就能通过await方法,恢复自己的任务。
这里的主线程是相对的概念,需要根据CountDownLatch创建的场景分析。
2、主要方法
特有方法:public CountDownLatch(int count); //指定计数的次数,只能被设置1次
public void countDown(); //调用此方法则计数减1
public void await() throws InterruptedException //调用此方法会一直阻塞当前线程,直到计时器的值为0,除非线程被中断。
Public Long getCount(); //得到当前的计数
Public boolean await(long timeout, TimeUnit unit) //调用此方法会一直阻塞当前线程,直到计时器的值为0,除非线程被中断或者计数器超时,返回false代表计数器超时。
From Object Inherited:
Clone、equals、hashCode、notify、notifyALL、wait等。
3、使用场景
(1)开启多个线程分块下载一个大文件,每个线程只下载固定的一截,最后由另外一个线程来拼接所有的分段。
(2)应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。
(3)确保一个计算不会执行,直到所需要的资源被初始化。
......
4、参考例子
public classCountDownLatchDemo {final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) throwsInterruptedException {
CountDownLatch latch=new CountDownLatch(2);//两个工人的协作
Worker worker1=new Worker("zhang san", 5000, latch);
Worker worker2=new Worker("li si", 8000, latch);
worker1.start();//
worker2.start();//
latch.await();//等待所有工人完成工作
System.out.println("all work done at "+sdf.format(newDate()));
}static class Worker extendsThread{
String workerName;intworkTime;
CountDownLatch latch;public Worker(String workerName ,intworkTime ,CountDownLatch latch){this.workerName=workerName;this.workTime=workTime;this.latch=latch;
}public voidrun(){
System.out.println("Worker "+workerName+" do work begin at "+sdf.format(newDate()));
doWork();//工作了
System.out.println("Worker "+workerName+" do work complete at "+sdf.format(newDate()));
latch.countDown();//工人完成工作,计数器减一
}private voiddoWork(){try{
Thread.sleep(workTime);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
View Code
class Driver { //...
void main() throwsInterruptedException {
CountDownLatch startSignal= new CountDownLatch(1);
CountDownLatch doneSignal= newCountDownLatch(N);for (int i = 0; i < N; ++i) //create and start threads
new Thread(newWorker(startSignal, doneSignal)).start();
doSomethingElse();//don't let run yet
startSignal.countDown(); //let all threads proceed
doSomethingElse();
doneSignal.await();//wait for all to finish
}
}class Worker implementsRunnable {private finalCountDownLatch startSignal;private finalCountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {this.startSignal =startSignal;this.doneSignal =doneSignal;
}public voidrun() {try{
startSignal.await();
doWork();
doneSignal.countDown();
}catch (InterruptedException ex) {} //return;
}voiddoWork() { ... }
}
View Code
注:
1. latch.countDown(); 建议放到finally语句里。
2. 对这个计数器的操作都是原子操作,同时只能有一个线程去操作这个计数器。
5、常见面试题
通过上述的介绍很容易解决下面的面试题:
(1)解释一下CountDownLatch概念?
(2)CountDownLatch 和CyclicBarrier的不同之处?
(3)给出一些CountDownLatch使用的例子?
(4) CountDownLatch 类中主要的方法?
6、参考资料