Java中的CountDownLatch闭锁详解
作者:小柴林
Java中的CountDownLatch闭锁
CountDownLatch是对synchronization拓展,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。
CountDownLatch用给定的计数初始化。
await属于阻塞方法,直到当前计数达到零,由于countDown方法被调用,然后释放所有await等待的线程,并立即返回线程后续的await调用逻辑。
CountDownLatch是一种一次性现象——计数无法重置。
如果需要重新设置计数的版本,可以考虑使用CyclicBarrier。
CountDownLatch是一种通用的同步工具,可以用于多种目的。
初始化一个count为1的CountDownLatch用作一个简单的on/off latch或gate:所有调用wait的线程都在gate处等待,直到它被调用countDown的线程打开。
一个初始化为N的CountDownLatch可以用来让一个线程等待,直到N个线程完成了某个操作,或者某个操作已经完成了N次。
CountDownLatch的一个有用特性是,它不要求调用countDown的线程等待计数达到零才继续,它只是防止任何线程继续等待,直到所有线程都通过。
示例用法:这里有两个类,其中一组工作线程使用两个倒计时锁存器:
第一个是启动信号,它阻止任何worker继续工作,直到驱动程序准备好让它们继续工作;
第二个是一个完成信号,它允许驱动程序等待所有worker完成。
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(); // 预备工作逻辑 startSignal.countDown(); // 让所有Worker开始工作 doSomethingElse(); doneSignal.await(); // 等待所有Worker完成自己逻辑 } } 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(); doWork(); doneSignal.countDown(); } catch (InterruptedException ex) {} // return; } void doWork() { ... } }
另一种典型的用法是将一个问题分成N个部分,用一个Runnable描述每个部分,该Runnable执行该部分并在锁存器上计数,然后将所有Runnables排队到一个执行器。当所有子部件都完成时,协调线程将能够通过并等待。(当线程必须以这种方式重复倒数时,使用CyclicBarrier。)
class Driver2 { // ... void main() throws InterruptedException { CountDownLatch doneSignal = new CountDownLatch(N); Executor e = ... for (int i = 0; i < N; ++i) // 创建和开启线程 e.execute(new WorkerRunnable(doneSignal, i)); doneSignal.await(); // 等待所有Worker完成逻辑操作 } } 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() { ... } }
内存一致性效应:在计数达到零之前,在另一个线程中调用countDown()之前的动作发生——在从相应的await()成功返回之后的动作发生之前。
CountDownLatch底层通过内部类Sync继承AbstractQueuedSynchronizer类;
计数器字段使用内存语义volatile修饰,private volatile int state;
到此这篇关于Java中的CountDownLatch闭锁详解的文章就介绍到这了,更多相关CountDownLatch闭锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!