CountDownLatch is a class that allows one thread to wait for another thread to complete a task.
For example, you can create 5 threads in the main thread to have certain tasks processed in parallel. At this time, the main thread executes the next code (statements) without waiting for other threads to exit. You can use CountDownLatch here to make it wait without executing the next code (statements).
As another example, since processing a task doesn`t require a lot of CPU resources, it can be done only on the main thread. However, if you wait for a process to run or wait for an event to occur outside of Network, etc., you may end up waiting indefinitely when no such event occurs. In this case, you can set Timeout so that other threads can perform this task and the main thread does not wait for the task if it exceeds a certain amount of time.
As an example, you can use CountDownLatch for your own purposes.
Let`s see how to use it by introducing an example of CountDownLatch.
How CountDownLatch Works
CountDownLatch can be created like this: Pass the number of Latch as an argument.
CountDownLatch countDownLatch = new CountDownLatch(5);
Calling countDown()
as follows will decrement the number of latches by 1.
countDownLatch.countDown();
await()
is the code that waits until the number in the latch becomes 0.
countDownLatch.await();
If countDown()
is called 5 times in another thread, Latch becomes 0, await()
does not wait any longer and executes the next code.
Wait for other threads to finish
The following is an example where the Main thread creates 5 threads and waits for the 5 threads to complete their tasks.
public class CountDownLatchExample {
public static void main(String args[]) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
List<Thread> workers = Stream
.generate(() -> new Thread(new Worker(countDownLatch)))
.limit(5)
.collect(toList());
System.out.println("Start multi threads (tid: "
+ Thread.currentThread().getId() + ")");
workers.forEach(Thread::start);
System.out.println("Waiting for some work to be finished (tid: "
+ Thread.currentThread().getId() + ")");
countDownLatch.await();
System.out.println("Finished (tid: "
+ Thread.currentThread().getId() + ")");
}
public static class Worker implements Runnable {
private CountDownLatch countDownLatch;
public Worker(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println("Do something (tid: " + Thread.currentThread().getId() + ")");
countDownLatch.countDown();
}
}
}
Running it produces the following output: The Main thread waits for all other threads to finish, and finally prints a log that says ‘Finished’ and exits.
Start multi threads (tid: 1)
Doing something (tid: 11)
Doing something (tid: 12)
Doing something (tid: 13)
Doing something (tid: 14)
Waiting for some work to be finished (tid: 1)
Doing something (tid: 15)
Finished (tid: 1)
In the above code, the following code is the code that creates and executes a thread. A thread was created and executed using Java8s Stream.
Also, we passed
countDownLatch` as an argument to each thread.
CountDownLatch countDownLatch = new CountDownLatch(5);
List<Thread> workers = Stream
.generate(() -> new Thread(new Worker(countDownLatch)))
.limit(5)
.collect(toList());
workers.forEach(Thread::start);
The following code waits for other threads to finish their work in the main thread.
Since the initial value of Latch is set to 5, the Main thread continues to wait until countDown()
is called 5 times.
countDownLatch.await();
Here is the Worker class. Each thread will execute Worker.run()
. When run()
is executed, it prints the log and calls countDown()
.
public static class Worker implements Runnable {
private CountDownLatch countDownLatch;
public Worker(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println("Do something (tid: " + Thread.currentThread().getId() + ")");
countDownLatch.countDown();
}
}
Wait for all threads to be ready
In the example above, the thread that runs first does the work first and then exits. If some tasks need to be processed at the same time, you can implement it like this:
It`s pretty much the same as the example above, but with 2 more latches to make it wait for all threads to be ready.
public class CountDownLatchExample2 {
public static void main(String args[]) throws InterruptedException {
CountDownLatch readyLatch = new CountDownLatch(5);
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch finishLatch = new CountDownLatch(5);
List<Thread> workers = Stream
.generate(() -> new Thread(new Worker(readyLatch,
startLatch, finishLatch)))
.limit(5)
.collect(toList());
System.out.println("Start multi threads (tid: "
+ Thread.currentThread().getId() + ")");
workers.forEach(Thread::start);
readyLatch.await();
System.out.println("Waited for ready and started doing some work (tid: "
+ Thread.currentThread().getId() + ")");
startLatch.countDown();
finishLatch.await();
System.out.println("Finished (tid: "
+ Thread.currentThread().getId() + ")");
}
public static class Worker implements Runnable {
private CountDownLatch readyLatch;
private CountDownLatch startLatch;
private CountDownLatch finishLatch;
public Worker(CountDownLatch readyLatch, CountDownLatch startLatch,
CountDownLatch finishLatch) {
this.readyLatch = readyLatch;
this.startLatch = startLatch;
this.finishLatch = finishLatch;
}
@Override
public void run() {
readyLatch.countDown();
try {
startLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Do something (tid: "
+ Thread.currentThread().getId() + ")");
finishLatch.countDown();
}
}
}
Three latches are created as follows, and readyLatch
and startLatch
are latches to make the thread wait until ready.
CountDownLatch readyLatch = new CountDownLatch(5);
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch finishLatch = new CountDownLatch(5);
List<Thread> workers = Stream
.generate(() -> new Thread(new Worker(readyLatch, startLatch, finishLatch)))
.limit(5)
.collect(toList());
Here is the run()
of the run()
class. When each thread is ready, it tells the main thread that it is ready with readyLatch.countDown()
.
And when the main thread calls startLatch.countDown()
, all threads start doing work.
@Override
public void run() {
readyLatch.countDown();
try {
startLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Do something (tid: " + Thread.currentThread().getId() + ")");
finishLatch.countDown();
}
The code below runs a thread on the main thread, and when it`s ready, it signals the latch to do something.
workers.forEach(Thread::start);
readyLatch.await();
System.out.println("Waited for ready and started doing some work (tid: "
+ Thread.currentThread().getId() + ")");
startLatch.countDown();
finishLatch.await();
System.out.println("Finished (tid: "
+ Thread.currentThread().getId() + ")");
The execution result is as follows.
Start multi threads (tid: 1)
Waited for ready and started doing some work (tid: 1)
Do something (tid: 12)
Do something (tid: 14)
Do something (tid: 13)
Do something (tid: 11)
Do something (tid: 15)
Finished (tid: 1)
Wait only for a set time (Timeout)
The problem with the above codes is that if a certain thread does not complete its work, countDown()
is not called and the main thread waits indefinitely.
By setting Timeout in await()
, you can make it wait only for a set amount of time.
You can set the Timeout like this: In the code below, Timeout is set to 5 seconds, and if Latch does not become 0 until 5 seconds have elapsed, the next code is executed without waiting. The unit of time can also be changed to MINUTES, etc.
await(5, TimeUnit.SECONDS)
The following is an example of setting Timeout.
public class CountDownLatchExample3 {
public static void main(String args[]) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
List<Thread> workers = Stream
.generate(() -> new Thread(new Worker(countDownLatch)))
.limit(5)
.collect(toList());
System.out.println("Start multi threads (tid: "
+ Thread.currentThread().getId() + ")");
workers.forEach(Thread::start);
System.out.println("Waiting for some work to be finished (tid: "
+ Thread.currentThread().getId() + ")");
countDownLatch.await(5, TimeUnit.SECONDS);
System.out.println("Finished (tid: "
+ Thread.currentThread().getId() + ")");
}
public static class Worker implements Runnable {
private CountDownLatch countDownLatch;
public Worker(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println("Doing something (tid: " + Thread.currentThread().getId() + ")");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
System.out.println("Done (tid: " + Thread.currentThread().getId() + ")");
}
}
}
Timeout is set to 5 seconds, and it makes Worker.run()
wait 10 seconds.
When I run it, it waits and gives up and executes the following code.
Start multi threads (tid: 1)
Doing something (tid: 11)
Doing something (tid: 12)
Doing something (tid: 13)
Doing something (tid: 14)
Waiting for some work to be finished (tid: 1)
Doing something (tid: 15)
Finished (tid: 1)
Done (tid: 12)
Done (tid: 11)
Done (tid: 14)
Done (tid: 15)
Done (tid: 13)
If you look at the result of this example, after 10 seconds, all other threads work has been completed. This is because we didn
t do anything to the thread when the timeout occurred.
If you want to terminate all threads when a timeout occurs, you can make Main terminate all other threads.
Related Posts
- Java - Remove items from List while iterating
- Java - How to find key by value in HashMap
- Java - Update the value of a key in HashMap
- Java - How to put quotes in a string
- Java - How to put a comma (,) after every 3 digits
- BiConsumer example in Java 8
- Java 8 - Consumer example
- Java 8 - BinaryOperator example
- Java 8 - BiPredicate Example
- Java 8 - Predicate example
- Java 8 - Convert Stream to List
- Java 8 - BiFunction example
- Java 8 - Function example
- Java - Convert List to Map
- Exception testing in JUnit
- Hamcrest Collections Matcher
- Hamcrest equalTo () Matcher
- AAA pattern of unit test (Arrange/Act/Assert)
- Hamcrest Text Matcher
- Hamcrest Custom Matcher
- Why Junit uses Hamcrest
- Java - ForkJoinPool
- Java - How to use Futures
- Java - Simple HashTable implementation
- Java - Create a file in a specific path
- Java - Mockito의 @Mock, @Spy, @Captor, @InjectMocks
- Java - How to write test code using Mockito
- Java - Synchronized block
- Java - How to decompile a ".class" file into a Java file (jd-cli decompiler)
- Java - How to generate a random number
- Java - Calculate powers, Math.pow()
- Java - Calculate the square root, Math.sqrt()
- Java - How to compare String (==, equals, compare)
- Java - Calculate String Length
- Java - case conversion & comparison insensitive (toUpperCase, toLowerCase, equalsIgnoreCase)