最近遇到一个问题,需要批量的从数据库中读取数据,但是对于数据的处理需要等待一部分完成之后再进行下一批的数据。抽象出来就是一个任务,分成N份来执行,每一份由多个线程去执行,一份完成之后再执行下一份。于是想起了CountDownLatch。
网上关于CountDownLatch的文章很多,搜了一些,也大体看了一下,但是感觉最好的还是CountDownLatch官方代码里面的两个例子,现在把这两个例子简单说明一下。
CountDownLatch一个线程同步的工具,是的一个或者多个线程等待其他线程操作完成之后再执行。
CountDownLatch通过一个给定的数值count来进行初始化,方法await()一直阻塞直到当前的count到达零为止,count的数值通过countDown()方法来减1,count的数值一旦设定就不能再修改,如果需要进行修改,请考虑使用CyclicBarrier。
大体看了一下源代码,是通过同步队列来作为计数器来进行控制的。同步队列是在CountDownLatch内部实现了一个静态内部类,countDown()通过调用队列来减1。
有两个典型的应用场景:
第一种是一个开始的信号,所有的task任务等待这个信号。类似于百米赛跑中的信号员,所有的运动员都做好准备,等待信号,信号一来,那就开始运行。
第二种是将一个任务分支N部分由M个线程来处理,等待所有的线程M完成任务后做其他的事情,还是刚才的例子,所有运动员跑完之后,才能知道所有人员的排名情况。
public class CountDownLatchTest {
public static void main(String[] args) throws Exception{
CountDownLatch s = new CountDownLatch(1);
CountDownLatch e = new CountDownLatch(6);
for(int i=0;i<6;i++){
new Thread(new Worker(s,e)).start();
}
System.out.println("i am the judge ,now ,i start the singal");
s.countDown();
System.out.println("waiting all task over"+e.getCount());
e.await();
System.out.println("all is over");
}
}
class Worker implements Runnable{
private final CountDownLatch startSingal ;
private final CountDownLatch endSingal;
public Worker(CountDownLatch startSingal, CountDownLatch endSingal) {
super();
this.startSingal = startSingal;
this.endSingal = endSingal;
}
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"waiting the start singal...."+startSingal.getCount());
//等待开始信号信号
startSingal.await();
System.out.println(Thread.currentThread().getName()+"start to executer");
//结束的计数器减一
endSingal.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
Thread-0waiting the start singal....1
Thread-2waiting the start singal....1
Thread-1waiting the start singal....1
i am the judge ,now ,i start the singal
waiting all task over6
Thread-1start to executer
Thread-3waiting the start singal....1
Thread-4waiting the start singal....1
Thread-3start to executer
Thread-5waiting the start singal....0
Thread-5start to executer
Thread-2start to executer
Thread-0start to executer
Thread-4start to executer
all is over
public static void main(String[] args) throws Exception{
CountDownLatch latch = new CountDownLatch(6);
Executor e = Executors.newFixedThreadPool(6);
System.out.println("thread number is 6,now start");
for(int i=0;i<6;i++){
e.execute(new Worker(latch,i));
}
System.out.println("waiting all is over ");
latch.await();
System.out.println("all is over");
}
}
class Worker implements Runnable{
private final CountDownLatch number;
private int temp;
public Worker(CountDownLatch number, int temp) {
super();
this.number = number;
this.temp = temp;
}
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName()+"runnable - "+temp);
number.countDown();
}
}
waiting all is over
pool-1-thread-4runnable - 3
pool-1-thread-5runnable - 4
pool-1-thread-1runnable - 0
pool-1-thread-3runnable - 2
pool-1-thread-2runnable - 1
pool-1-thread-6runnable - 5
all is over