有人能帮助我理解什么是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.");
}
}
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();
}
}
}
如果你在调用latch.countDown()后添加一些调试,这可能会帮助你更好地理解它的行为。
latch.countDown();
System.out.println("DONE "+this.latch); // Add this debug
输出将显示Count正在递减。这个“计数”实际上是你启动的可运行任务(处理器对象)的数量,其中countDown()没有被调用,因此在主线程调用latch.await()时被阻塞。
DONE java.util.concurrent.CountDownLatch@70e69696[Count = 2]
DONE java.util.concurrent.CountDownLatch@70e69696[Count = 1]
DONE java.util.concurrent.CountDownLatch@70e69696[Count = 0]
来自oracle关于CountDownLatch的文档:
一种同步辅助工具,允许一个或多个线程等待在其他线程中执行的一组操作完成。
CountDownLatch使用给定的计数进行初始化。由于调用了countDown()方法,await方法阻塞直到当前计数为零,在此之后所有等待的线程都被释放,并且await的任何后续调用立即返回。这是一个一次性现象——计数不能重置。
CountDownLatch是一种通用的同步工具,可用于许多目的。
初始化为1的CountDownLatch用作简单的开/关锁存器或门:所有调用await的线程都在门处等待,直到由调用countDown()的线程打开。
初始化为N的CountDownLatch可用于使一个线程等待到N个线程完成某个操作,或者某个操作已完成N次。
public void await()
throws InterruptedException
导致当前线程等待,直到锁存器计数到零,除非线程被中断。
如果当前计数为零,则此方法立即返回。
public void countDown()
减少锁存器的计数,如果计数为零,则释放所有等待线程。
如果当前计数大于零,则递减。如果新的计数为零,则所有等待的线程都将重新启用以进行线程调度。
解释你的例子。
You have set count as 3 for latch variable
CountDownLatch latch = new CountDownLatch(3);
You have passed this shared latch to Worker thread : Processor
Three Runnable instances of Processor have been submitted to ExecutorService executor
Main thread ( App ) is waiting for count to become zero with below statement
latch.await();
Processor thread sleeps for 3 seconds and then it decrements count value with latch.countDown()
First Process instance will change latch count as 2 after it's completion due to latch.countDown().
Second Process instance will change latch count as 1 after it's completion due to latch.countDown().
Third Process instance will change latch count as 0 after it's completion due to latch.countDown().
Zero count on latch causes main thread App to come out from await
App program prints this output now : Completed
CountDownLatch在Java中是一种同步器,它允许一个线程在开始处理之前等待一个或多个线程。
CountDownLatch工作在闩锁原理,线程将等待直到门打开。在创建CountDownLatch时,一个线程等待指定的n个线程。
例如:final CountDownLatch = new CountDownLatch(3);
这里我们将计数器设置为3。
任何调用CountDownLatch.await()的线程(通常是应用程序的主线程)都将等待,直到count为零或被另一个线程中断。所有其他线程都需要在完成或准备工作后调用CountDownLatch.countDown()来进行倒计时。一旦count达到0,等待的线程就开始运行。
这里的计数是通过CountDownLatch.countDown()方法递减的。
调用await()方法的线程将一直等待,直到初始计数为零。
为了使计数为零,其他线程需要调用countDown()方法。
一旦计数为零,调用await()方法的线程将恢复(开始执行)。
CountDownLatch的缺点是它不可重用:一旦计数为零,它就不再可用。
是的,你理解对了。
CountDownLatch的工作原理是闩锁,主线程将等待门被打开。一个线程等待n个线程,这是在创建CountDownLatch时指定的。
任何调用CountDownLatch.await()的线程(通常是应用程序的主线程)都将等待,直到count达到零或被另一个线程中断。所有其他线程都需要在完成或准备就绪后调用CountDownLatch.countDown()进行倒计时。
只要count达到0,等待线程就会继续。CountDownLatch的一个缺点/优点是它不可重用:一旦count达到0,就不能再使用CountDownLatch了。
编辑:
当一个线程(如主线程)需要等待一个或多个线程完成后才能继续处理时,使用CountDownLatch。
在Java中使用CountDownLatch的一个经典示例是一个服务器端核心Java应用程序,该应用程序使用服务体系结构,其中多个服务由多个线程提供,在所有服务成功启动之前应用程序不能开始处理。
注:
OP的问题有一个非常简单的例子,所以我没有包括一个。
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();
}
}
}