有人能帮助我理解什么是Java CountDownLatch以及什么时候使用它吗?
我不太清楚这个项目是怎么运作的。据我所知,这三个线程同时开始,每个线程将在3000ms后调用CountDownLatch。所以倒数会一个一个递减。锁存变为零后,程序打印“完成”。也许我理解的方式是错误的。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class Processor implements Runnable {
private CountDownLatch latch;
public Processor(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
System.out.println("Started.");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
}
// -----------------------------------------------------
public class App {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(3); // coundown from 3 to 0
ExecutorService executor = Executors.newFixedThreadPool(3); // 3 Threads in pool
for(int i=0; i < 3; i++) {
executor.submit(new Processor(latch)); // ref to latch. each time call new Processes latch will count down by 1
}
try {
latch.await(); // wait until latch counted down to 0
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Completed.");
}
}
这个来自Java 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(); // don't let run yet
startSignal.countDown(); // let all threads proceed
doSomethingElse();
doneSignal.await(); // wait for all to finish
}
}
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() { ... }
}
视觉解释:
显然,CountDownLatch允许一个线程(这里是Driver)等待一堆正在运行的线程(这里是Worker)完成它们的执行。
CountDownLatch在Java中是一种同步器,它允许一个线程在开始处理之前等待一个或多个线程。
CountDownLatch工作在闩锁原理,线程将等待直到门打开。在创建CountDownLatch时,一个线程等待指定的n个线程。
例如:final CountDownLatch = new CountDownLatch(3);
这里我们将计数器设置为3。
任何调用CountDownLatch.await()的线程(通常是应用程序的主线程)都将等待,直到count为零或被另一个线程中断。所有其他线程都需要在完成或准备工作后调用CountDownLatch.countDown()来进行倒计时。一旦count达到0,等待的线程就开始运行。
这里的计数是通过CountDownLatch.countDown()方法递减的。
调用await()方法的线程将一直等待,直到初始计数为零。
为了使计数为零,其他线程需要调用countDown()方法。
一旦计数为零,调用await()方法的线程将恢复(开始执行)。
CountDownLatch的缺点是它不可重用:一旦计数为零,它就不再可用。
One good example of when to use something like this is with Java Simple Serial Connector, accessing serial ports. Typically you'll write something to the port, and asyncronously, on another thread, the device will respond on a SerialPortEventListener. Typically, you'll want to pause after writing to the port to wait for the response. Handling the thread locks for this scenario manually is extremely tricky, but using Countdownlatch is easy. Before you go thinking you can do it another way, be careful about race conditions you never thought of!!
伪代码:
CountDownLatch latch;
void writeData() {
latch = new CountDownLatch(1);
serialPort.writeBytes(sb.toString().getBytes())
try {
latch.await(4, TimeUnit.SECONDS);
} catch (InterruptedException e) {
}
}
class SerialPortReader implements SerialPortEventListener {
public void serialEvent(SerialPortEvent event) {
if(event.isRXCHAR()){//If data is available
byte buffer[] = serialPort.readBytes(event.getEventValue());
latch.countDown();
}
}
}
As mentioned in JavaDoc (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html), CountDownLatch is a synchronization aid, introduced in Java 5. Here the synchronization does not mean restricting access to a critical section. But rather sequencing actions of different threads.
The type of synchronization achieved through CountDownLatch is similar to that of Join.
Assume that there is a thread "M" which needs to wait for other worker threads "T1", "T2", "T3" to complete its tasks
Prior to Java 1.5, the way this can be done is, M running the following code
T1.join();
T2.join();
T3.join();
上面的代码确保线程M在T1、T2、T3完成它的工作之后恢复它的工作。T1, T2, T3可以按任意顺序完成它们的工作。
同样可以通过CountDownLatch实现,其中T1、T2、T3和线程M共享相同的CountDownLatch对象。
"M"请求:countDownLatch.await();
其中“T1”,“T2”,“T3”是countDownLatch.countdown();
连接方法的一个缺点是M必须知道T1、T2、T3。如果后来添加了一个新的工作线程T4,那么M也必须知道它。使用CountDownLatch可以避免这种情况。
实现后的动作顺序为[T1,T2,T3](T1,T2,T3的顺序可以任意)-> [M]