可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Can someone help me to understand what Java CountDownLatch
is and when to use it?
I don't have a very clear idea of how this program works. As I understand all three threads start at once and each Thread will call CountDownLatch after 3000ms. So count down will decrement one by one. After latch becomes zero the program prints "Completed". Maybe the way I understood is incorrect.
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.");
}
}
回答1:
Yes, you understood correctly.
CountDownLatch
works in latch principle, the main thread will wait until the gate is open. One thread waits for n threads, specified while creating the CountDownLatch
.
Any thread, usually the main thread of the application, which calls CountDownLatch.await()
will wait until count reaches zero or it's interrupted by another thread. All other threads are required to count down by calling CountDownLatch.countDown()
once they are completed or ready.
As soon as count reaches zero, the waiting thread continues. One of the disadvantages/advantages of CountDownLatch
is that it's not reusable: once count reaches zero you cannot use CountDownLatch
any more.
Edit:
Use CountDownLatch
when one thread (like the main thread) requires to wait for one or more threads to complete, before it can continue processing.
A classical example of using CountDownLatch
in Java is a server side core Java application which uses services architecture, where multiple services are provided by multiple threads and the application cannot start processing until all services have started successfully.
P.S.
OP's question has a pretty straightforward example so I didn't include one.
回答2:
CountDownLatch
in Java is a type of synchronizer which allows one Thread
to wait for one or more Thread
s before it starts processing.
CountDownLatch
works on latch principle, thread will wait until gate is open. One thread waits for n
number of threads specified while creating CountDownLatch
.
e.g. final CountDownLatch latch = new CountDownLatch(3);
Here we set the counter to 3.
Any thread, usually main thread of application, which calls CountDownLatch.await()
will wait until count reaches zero or it's interrupted by another Thread
. All other threads are required to do count down by calling CountDownLatch.countDown()
once they are completed or ready to the job. as soon as count reaches zero, the Thread
awaiting starts running.
Here the count is get decremented by CountDownLatch.countDown()
method.
The Thread
which calls the await()
method will wait until the initial count reaches to zero.
To make count zero other threads need to call the countDown()
method.
Once the count become zero the thread which invoked the await()
method will resume (start its execution).
The disadvantage of CountDownLatch
is that it's not reusable: once the count become zero it is no longer usable.
回答3:
NikolaB explained it very well, However example would be helpful to understand, So here is one simple example...
import java.util.concurrent.*;
public class CountDownLatchExample {
public static class ProcessThread implements Runnable {
CountDownLatch latch;
long workDuration;
String name;
public ProcessThread(String name, CountDownLatch latch, long duration){
this.name= name;
this.latch = latch;
this.workDuration = duration;
}
public void run() {
try {
System.out.println(name +" Processing Something for "+ workDuration/1000 + " Seconds");
Thread.sleep(workDuration);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+ "completed its works");
//when task finished.. count down the latch count...
// basically this is same as calling lock object notify(), and object here is latch
latch.countDown();
}
}
public static void main(String[] args) {
// Parent thread creating a latch object
CountDownLatch latch = new CountDownLatch(3);
new Thread(new ProcessThread("Worker1",latch, 2000)).start(); // time in millis.. 2 secs
new Thread(new ProcessThread("Worker2",latch, 6000)).start();//6 secs
new Thread(new ProcessThread("Worker3",latch, 4000)).start();//4 secs
System.out.println("waiting for Children processes to complete....");
try {
//current thread will get notified if all chidren's are done
// and thread will resume from wait() mode.
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Process Completed....");
System.out.println("Parent Thread Resuming work....");
}
}
回答4:
It is used when we want to wait for more than one thread to complete its task. It is similar to join in threads.
Where we can use CountDownLatch
Consider a scenario where we have requirement where we have three threads "A", "B" and "C" and we want to start thread "C" only when "A" and "B" threads completes or partially completes their task.
It can be applied to real world IT scenario
Consider a scenario where manager divided modules between development teams (A and B) and he wants to assign it to QA team for testing only when both the teams completes their task.
public class Manager {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
MyDevTeam teamDevA = new MyDevTeam(countDownLatch, "devA");
MyDevTeam teamDevB = new MyDevTeam(countDownLatch, "devB");
teamDevA.start();
teamDevB.start();
countDownLatch.await();
MyQATeam qa = new MyQATeam();
qa.start();
}
}
class MyDevTeam extends Thread {
CountDownLatch countDownLatch;
public MyDevTeam (CountDownLatch countDownLatch, String name) {
super(name);
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println("Task assigned to development team " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("Task finished by development team Thread.currentThread().getName());
this.countDownLatch.countDown();
}
}
class MyQATeam extends Thread {
@Override
public void run() {
System.out.println("Task assigned to QA team");
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("Task finished by QA team");
}
}
Output of above code will be:
Task assigned to development team devB
Task assigned to development team devA
Task finished by development team devB
Task finished by development team devA
Task assigned to QA team
Task finished by QA team
Here await() method waits for countdownlatch flag to become 0, and countDown() method decrements countdownlatch flag by 1.
Limitation of JOIN:
Above example can also be achieved with JOIN, but JOIN can not be used in two scenarios:
- When we use ExecutorService instead of Thread class to create threads.
- Modify above example where Manager wants to handover code to QA team as soon as Development completes their 80% task. It means that CountDownLatch allow us to modify implementation which can be used to wait for another thread for their partial execution.
回答5:
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!!
Pseudocode:
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();
}
}
}
回答6:
CoundDownLatch enables you to make a thread wait till all other threads are done with their execution.
Pseudo code can be:
// Main thread starts
// Create CountDownLatch for N threads
// Create and start N threads
// Main thread waits on latch
// N threads completes there tasks are returns
// Main thread resume execution
回答7:
If you add some debug after your call to latch.countDown(), this may help you understand its behaviour better.
latch.countDown();
System.out.println("DONE "+this.latch); // Add this debug
The output will show the Count being decremented. This 'count' is effectively the number of Runnable tasks (Processor objects) you've started against which countDown() has not been invoked and hence is blocked the main thread on its call to 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]
回答8:
From oracle documentation about CountDownLatch:
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
A CountDownLatch
is initialized with a given count. The await
methods block until the current count reaches zero due to invocations of the countDown()
method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset.
A CountDownLatch is a versatile synchronization tool and can be used for a number of purposes.
A CountDownLatch
initialized with a count of one serves as a simple on/off latch, or gate: all threads invoking await wait at the gate until it is opened by a thread invoking countDown().
A CountDownLatch
initialized to N can be used to make one thread wait until N threads have completed some action, or some action has been completed N times.
public void await()
throws InterruptedException
Causes the current thread to wait until the latch has counted down to zero, unless the thread is interrupted.
If the current count is zero then this method returns immediately.
public void countDown()
Decrements the count of the latch, releasing all waiting threads if the count reaches zero.
If the current count is greater than zero then it is decremented. If the new count is zero then all waiting threads are re-enabled for thread scheduling purposes.
Explanation of your example.
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
回答9:
This example from Java Doc helped me understand the concepts clearly:
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() { ... }
}
Visual interpretation:
Evidently, CountDownLatch
allows one thread (here Driver
) to wait until a bunch of running threads (here Worker
) are done with their execution.
回答10:
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();
The above code makes sure that thread M resumes its work after T1, T2, T3 completes its work. T1, T2, T3 can complete their work in any order.
The same can be achieved through CountDownLatch, where T1,T2, T3 and thread M share same CountDownLatch object.
"M" requests : countDownLatch.await();
where as "T1","T2","T3" does countDownLatch.countdown();
One disadvantage with the join method is that M has to know about T1, T2, T3. If there is a new worker thread T4 added later, then M has to be aware of it too. This can be avoided with CountDownLatch.
After implementation the sequence of action would be [T1,T2,T3](the order of T1,T2,T3 could be anyway) -> [M]
回答11:
Best real time Example for countDownLatch explained in this link CountDownLatchExample
回答12:
package practice;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch c= new CountDownLatch(3); // need to decrements the count (3) to zero by calling countDown() method so that main thread will wake up after calling await() method
Task t = new Task(c);
Task t1 = new Task(c);
Task t2 = new Task(c);
t.start();
t1.start();
t2.start();
c.await(); // when count becomes zero main thread will wake up
System.out.println("This will print after count down latch count become zero");
}
}
class Task extends Thread{
CountDownLatch c;
public Task(CountDownLatch c) {
this.c = c;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
c.countDown(); // each thread decrement the count by one
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}