Explain Codes LogoExplain Codes Logo

How is CountDownLatch used in Java Multithreading?

java
multithreading
synchronization
executor-service
Alex KataevbyAlex Kataev·Sep 22, 2024
TLDR

CountDownLatch allows threads to wait for other threads' tasks to complete before proceeding. This is done by threads decrementing a count object (i.e., countDown()), and others waiting until this count to reach zero (i.e., await()).

CountDownLatch latch = new CountDownLatch(2); // Waiting on 2 operations // Worker thread 1 new Thread(() -> { // Hopefully you're good with your work! latch.countDown(); // Operation done, time for a break! }).start(); // Worker thread 2, ditto new Thread(() -> { // Don't spill your coffee on the keyboard... latch.countDown(); // Done! Coffee break! }).start(); // Main thread, anxiously waiting for workers latch.await(); // Have they finished yet? System.out.println("Finally, they did it... Main thread now resumes control");

The CountDownLatch here provides simple yet effective synchronization between threads.

Efficient Synchronization in Multithreaded Scenarios

Assuring Task Completion before Proceeding

CountDownLatch excels where certain actions in a main thread have to wait for multiple associated tasks in other threads to complete. Consider it as a strict team lead ensuring the team finishes their tasks before calling a meeting.

One-time Synchronization

The CountDownLatch is a one-and-done - once the count hits zero, it cannot be reset. This strict behavior is key to providing precise, reliable synchronization points in your code.

Seamless Integration with Executor Services

Coordination with ExecutorService

CountDownLatch marries well with the ExecutorService interface, adding a supplementary layer of synchronization on top of a managed pool of threads, saving you from the usual threading headaches.

ExecutorService executor = Executors.newFixedThreadPool(2); CountDownLatch latch = new CountDownLatch(2); // Submit tasks to executor for (int i = 0; i < 2; i++) { executor.submit(() -> { try { // Pretend we're busy doing something... } finally { latch.countDown(); } }); } // We can't shut down yet... latch.await(); // Boom! Now we're done! executor.shutdown();

This example demonstrates how the CountDownLatch coordinates the completion of tasks executed by a thread pool before it is shutting down.

Simulation of a CountDownLatch

Visualize a CountDownLatch as a school bus (🚌) can only depart after all students (👥) are onboard:

CountDownLatch(3) // Three students are missing // Students boarding: 👥 .countDown(); // We got one! 👥 .countDown(); // Another one's here! 👥 .countDown(); // Last one accounted for! Time to leave... 🚌 -> All aboard, yeet!

Here, each .countDown() denotes a thread finishing its task, only then the main thread can proceed after all threads have finished their tasks.

More Than Just Waiting for Threads

More Control Than thread.join()

Contrasting Thread.join(), the versatility of CountDownLatch provides more fine-grained synchronization while allowing a certain degree of parallel processing.

Caught Up in a Deadlock? Here's a Trick

Gosh, you're stuck in a deadlock! Don't worry, make sure the countDown() method is called in a finally block. This ensures it always executes, even if the try block throws exceptions - talk about playing safe!

try { // Calculates answer to life, universe, and everything... } finally { latch.countDown(); // On second thought, let's not count down. It's a silly place. }

Your Swiss Army Knife

Designed for real-world scenarios, CountDownLatch is essential where tasks must finish before other tasks can start - think of it your Swiss army knife for multi-threaded programming.