CountDownLatch的理解使用
一、介绍
CountDownLatch是jdk1.5中引入的一种同步辅助,允许一个或者多个线程等待,知道在其他线程中执行的一组操作完成。
CountDownLatch使用给定的计数器进行初始化,由于调用了countDown方法,await方法会阻塞,知道当前计数器达到为零,然后释放所有等待线程,并且任何后续的await方法都会立即返回,由于在初始化时指定了计数数量,所以无法重置技术,如果需要重置计数,需要使用CyclicBarrier.
计数为1的CountDownLatch可用于简单的锁,调用await的所有线程在等待,直到它被调用countDown的线程打开,初始化为N的线程可用于使一个主线程等待,直到N个线程完成操作执行完成,或者某个操作已完成N次。使用场景最多的可能是主线程等待所有子线程是否执行完毕。
简单来说,CountDownLatch类是一个基于AbstractQueuedSynchronizer(AQS)实现的计数器,可以设置初始化线程数(设置完后不可改变),在子线程结束时调用countDown()方法可以使计数数量减1,当所有线程结束,即最终getCount()为0时,会调用CountDownLatch的成员方法wait()的线程就会取消BLOCKED阻塞状态,进去RUNNABLE从而继续执行。
二、使用
下面来看源码里举的例子,我理解的是四个搬运工装车
import java.util.concurrent.CountDownLatch;
/**
* 司机
* @author
*/
public class Driver {
public static void main(String[] args) throws InterruptedException {
int N = 4;
// 开始装车信号
CountDownLatch startSignal = new CountDownLatch(1);
// 装车完毕信号
CountDownLatch doneSignal = new CountDownLatch(N);
// 初始化四个搬运工
for (int i = 0; i < N; ++i) {
new Thread(new WorkerRunnable(startSignal, doneSignal), "Worker-" + i).start();
}
// 司机做准备工作,比如把车开到指定地方
doSomethingElse();
// 准备完了,发信号让开始搬运
startSignal.countDown();
// 司机等待所有搬运工完成
doneSignal.await();
// 完成
doSomethingElse2();
}
static void doSomethingElse() throws InterruptedException {
Thread.sleep(1000);
System.out.println("司机准备工作完成");
}
static void doSomethingElse2() throws InterruptedException {
Thread.sleep(1000);
System.out.println("滴,滴滴~ 老司机发车了");
}
}
import java.util.Random; import java.util.concurrent.CountDownLatch; /** * 搬运工 * @author 15641 */ public class WorkerRunnable implements Runnable{ private final CountDownLatch startSignal; private final CountDownLatch doneSignal; public WorkerRunnable(CountDownLatch startSignal, CountDownLatch doneSignal) { this.startSignal = startSignal; this.doneSignal = doneSignal; } @Override public void run() { try { // 搬运工等待准备工作完成 startSignal.await(); // 开始工作 doWork(); // 装完之后给司机发个信号 doneSignal.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } void doWork() throws InterruptedException { // 模拟每个工人的装车速度 Random random = new Random(); Thread.sleep((long)(random.nextDouble() * 5000)); System.out.println(Thread.currentThread().getName() + "装完了"); } }
执行结果为
上述例子是多个主线程等待多个线程全部执行完,第二个例子是使用线程池用一个Runnable重复执行每一部分
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author 15641 */ public class Driver2 { public static void main(String[] args) throws InterruptedException { int N = 4; CountDownLatch doneSignal = new CountDownLatch(N); ExecutorService service = Executors.newFixedThreadPool(N); for (int i = 0; i < N; ++i) { service.submit(new WorkerRunnable2(doneSignal, i)); } doneSignal.await(); } }
import java.util.Random; import java.util.concurrent.CountDownLatch; /** * @author 15641 */ public class WorkerRunnable2 implements Runnable{ private final CountDownLatch doneSingal; private final int i; public WorkerRunnable2(CountDownLatch doneSingal, int i) { this.doneSingal = doneSingal; this.i = i; } @Override public void run() { try { doWork(i); doneSingal.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } void doWork(int i) throws InterruptedException { // 模拟每个工人的装车速度 Random random = new Random(); Thread.sleep((long)(random.nextDouble() * 8000)); System.out.println("任务" + i + "完成了"); } }