java.util.concurrent.CountDownLatch
介绍:
CountDownLatch是一个开闭锁,允许多个线程同时阻塞在一个点,然后通过信号量来同时释放线程。就好像赛马比赛:所有的马(Thread)都等着栅栏(Singal)放开那一刻,放开后,奋力前行,到达终点(CPU或者其他资源)。
CountDownLatch初始化方法为:CountDownLatch(int count) ;需要给定一个计数器,这个计数器就是我们预期等待的线程个数。当调用CountDownLatch.await方法时,回去判断这个count是否为0,如果为0了则不会阻塞线程,如果不为0则阻塞。计数器count的值通过Co Latch.countDown()方法来减少。
1、CountDownLatch可以做并发测试。
比如:有一个服务UserService.addUser(String name)的流程为:先查询name是否存在,如果存在则返回false,否则返回true。这个接口需要确保当N个线程调用时,只有一个name可以插入。
//这个例子在CountDownLatch的doc中,我只是将其改成具有业务含义的例子。
class Driver {
void main() throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1); //线程的栅栏,使所有现在都在某个点阻塞。
CountDownLatch doneSignal = new CountDownLatch(N); //并发数
for (int i = 0; i < N; ++i) // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
doSomethingElse(); // do sth
startSignal.countDown(); // let all threads proceed
doSomethingElse();
doneSignal.await(); // wait for all to finish
assertOnlyOneUserName(); //所有线程都执行完成后,我们需要验证,只有一个Name存在。
}
}
//测试中,这个工作线程就是去调用addUser服务的。 class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await();//所有线程必须等待主线程释放栅栏,释放后N个线程一起去抢占资源。
doWork();
doneSignal.countDown(); //N个线程全部完成后,唤醒主线程,让其验证N个线程调用服务后的结果。
} catch (InterruptedException ex) {} // return;
}
void doWork() {
UserService.addUser(xxx) ; //调用需要并发测试的服务
}
}
//以上只是一个简单的控制多个线程同时并发的例子。如果想做更精细的代码段测试,可以通过代码动/态编译技术,植入代码片段,让所有的线程都阻塞,然后通过某个信号量释放所有线程,达到并发目的。
2、CountDownLatch另一个用法可能是多线程中做分片处理(分片任务的实现有很多,CountDownLatch只是一种)。
class Driver2 { // ...这个例子来自JDK的doc
void main() throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(N);
Executor e = ...
for (int i = 0; i < N; ++i) // create and start threads
e.execute(new WorkerRunnable(doneSignal, i));//这里其实是将资源分片,交给worker处理
doneSignal.await(); // wait for all to finish
}
}
class WorkerRunnable implements Runnable {
private final CountDownLatch doneSignal;
private final int i;
WorkerRunnable(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
}
public void run() {
try {
doWork(i);
doneSignal.countDown();
} catch (InterruptedException ex) {} // return;
}
void doWork() { ... }
}
CountDownLatch是AQS共享锁的一个业务实现类。
public int tryAcquireShared(int acquires) {
return getState() == 0? 1 : -1;//这个方法主要是为了根据计数器来阻塞线程。
}
public boolean tryReleaseShared(int releases) { //如果计数器达到0则释放其他等待线程
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void countDown() {
sync.releaseShared(1);
}