Using CountDownLatch to implement multi-thread collaboration

Directory

Foreword

In multi-threaded programming, it is often necessary to implement a mechanism to coordinate the execution of multiple threads to ensure that certain operations are performed after all threads have completed. CountDownLatch is a synchronization tool provided in the Java concurrency package, which allows one or more threads to wait for other threads to complete operations.

Learn about CountDownLatch

Summary

CountDownLatch is a synchronization auxiliary class introduced in Java 1.5. When constructing, you need to specify a count value, which represents the number of events to wait for. Whenever an event completes, the count value is decremented by one. When the count value reaches zero, the waiting thread is awakened to continue execution.

Application scenarios of CountDownLatch

CountDownLatch can be widely used in various multi-thread collaboration scenarios, such as:

  • The main thread waits for multiple child threads to complete before performing the next step.
  • Multiple subtasks are executed in parallel, and the results are finally merged.
  • In parallel computing, wait for all computing tasks to be completed before performing unified summarization.

Use cases

Let us understand the use of CountDownLatch through a sample code. Suppose there is a task that needs to be assigned to multiple sub-threads to complete, and the main thread needs to wait for all sub-threads to finish executing before continuing.

//Number of threads for task division
private static final int THREAD_TOTAL = 10;

//Timeout period for child thread execution
private static final int countDownLatchTimeout = 5;

public static void main(String[] args) {<!-- -->
    //Create CountDownLatch and set the count value. The count value can be set according to the number of threads.
    CountDownLatch countDownLatch = new CountDownLatch(THREAD_TOTAL);

    //Create a thread pool, start and create asynchronous threads to execute tasks
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    for (int i = 0; i <thREAD_TOTAL; i + + ) {<!-- -->
        cachedThreadPool.execute(() -> {<!-- -->
            try {<!-- -->
                Thread.sleep(5000);
                System.out.println(Thread.currentThread().getName() + " do something!");
            } catch (Exception e) {<!-- -->
                System.out.println("Exception: do something exception");
            } finally {<!-- -->
                //The thread has completed execution -1
                countDownLatch.countDown();
            }
        });
    }

    //Return to the main thread
    System.out.println("Back main thread do something");
    try {<!-- -->
        //The main thread waits for completion in the thread pool (sub-thread execution timeout)
        boolean await = countDownLatch.await(countDownLatchTimeout, TimeUnit.MINUTES);
        System.out.println(await);
    } catch (InterruptedException e) {<!-- -->
        System.out.println("Exception: await interrupted exception");
    } finally {<!-- -->
        System.out.println("countDownLatch: " + countDownLatch);
    }
    System.out.println("main thread do something-2");
}

Analysis of advantages and disadvantages of CountDownLatch

Advantages

  • Simple and easy to use: CountDownLatch is very simple to use, and multi-thread collaboration can be achieved through the await and countDown methods.
  • Flexibility: The waiting count value can be specified according to specific scenarios, and the collaboration relationship of multiple threads can be flexibly controlled.
  • Efficiency: The bottom layer uses AQS (AbstractQueuedSynchronizer) to achieve synchronization, which can ensure efficient coordination of the execution sequence of multiple threads.

Disadvantages

  • One-time: The count value of CountDownLatch can only be decremented and cannot be reset. Once the count reaches zero, it cannot be used again.
  • Cannot cancel midway: Once the wait starts, it cannot be canceled midway unless the wait times out or is interrupted.

pexels-bar-kelleci-18888635.jpg

If you have room for learning or no urgent needs at hand, please continue reading below. Let us simply analyze the implementation of CountDownLatch from the source code level.

Analysis of the implementation of CountDownLatch from the source code level

Implementation

I intercepted the key internal implementation logic of CountDownLatch to analyze its implementation principle:

The function of CountDownLatch is mainly implemented through the internal class Sync. In the internal class, Sync inherits from AbstractQueuedSynchronizer to achieve synchronization. Operation, AbstractQueuedSynchronizer provides the basic framework for synchronizer implementation. Through this class, developers can relatively easily implement custom synchronizers, such as exclusive locks, shared locks, semaphores, etc.

/**
 * Synchronization control For CountDownLatch.
 * Uses AQS state to represent count.
 */
private static final class Sync extends AbstractQueuedSynchronizer {<!-- -->
    private static final long serialVersionUID = 4982264981922014374L;

    Sync(int count) {<!-- -->
        setState(count);
    }

    int getCount() {<!-- -->
        return getState();
    }

    protected int tryAcquireShared(int acquires) {<!-- -->
        return (getState() == 0) ? 1 : -1;
    }

    protected boolean tryReleaseShared(int releases) {<!-- -->
        // Decrement count; signal when transition to zero
        for (;;) {<!-- -->
            int c = getState();
            if(c==0)
                return false;
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }
}

private final Sync sync;


public CountDownLatch(int count) {<!-- -->
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}


public void await() throws InterruptedException {<!-- -->
    sync.acquireSharedInterruptibly(1);
}
  • Sync: Defines a static internal class named Sync, which inherits from the AbstractQueuedSynchronizer class. This class is usually used to implement locks and related synchronizers.
  • count: Defines a serialization version number for version control during object serialization and deserialization. At the same time, count is used to set the current status in the constructor of CountDownLatch, that is: the count value passed in by the coder.
  • getCount: Get the current status value, that is, the remaining count value.
  • tryAcquireShared: Try to acquire shared resources. If the current status is 0, return 1 to indicate successful resource acquisition, otherwise return -1 to indicate failure to acquire resources.

tryReleaseShared

protected boolean tryReleaseShared(int releases) {<!-- -->
    // Decrement count; signal when transition to zero
    for (;;) {<!-- -->
        int c = getState();
        if(c==0)
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

The tryReleaseShared method attempts to release shared resources. It first tries continuously through an infinite loop and obtains the current status value in the loop. If the status value is already 0, it returns false directly; otherwise, it decrements the status value by 1 and attempts to set the status value atomically. , if the setting is successful, it returns whether the status value becomes 0, otherwise the loop continues.

In general, this code implements a simple CountDownLatch function, trying to acquire shared resources through the tryAcquireShared method, and trying to release shared resources through the tryReleaseShared method. When the status value of the shared resource is 0, it means that all waiting threads have been released.

Extension

CompletableFuture brief description

After JDK 1.8, the java.util.concurrent package provides the CompletableFuture class to support asynchronous programming and asynchronous task processing, compared to CountDownLatch, which provides a richer API. Personally, I prefer CompletableFuture because it is highly scalable, more suitable for the functional programming features provided by JDK 8, and the code is more elegant.

Advantages and disadvantages of CompletableFuture

Advantages
  • Powerful: CompletableFuture provides a wealth of methods and combination operations to implement complex asynchronous programming logic.
  • Support exception handling: Exception situations in asynchronous operations can be conveniently handled through the exceptionally or handle method.
  • Supports combination operations: Combination operations of multiple CompletableFutures can be easily performed through thenCompose, thenCombine and other methods.
Disadvantages
  • Steep learning curve: Compared with CountDownLatch, the use of CompletableFuture may require more learning and understanding of asynchronous programming concepts.
  • High complexity: In complex business scenarios, problems such as nested callbacks and difficulty in exception handling may occur, which increases the complexity of the code.

Summary

CountDownLatch and CompletableFuture are both tools for multi-thread collaboration in Java, and they are each suitable for different scenarios. CountDownLatch is more suitable for simple multi-thread collaboration, while CompletableFuture is more suitable for complex asynchronous programming scenarios. In practical applications, we can choose appropriate tools to implement multi-thread collaboration and asynchronous programming according to specific needs to achieve better development efficiency and code quality.

About me

Hello, I am Debug.c. WeChat public account: The maintainer of Zhongke Code Technology Tree is a cross-professional self-taught Java bugger with a passion for technology. He is also a Java Coder who has worked hard in a second-tier city for more than four years.

In the Nuggets, CSDN, and public accounts, I will share what I have learned recently, the pitfalls I have encountered, and my understanding of technology.

If you are interested in me, please contact me.

If you find anything useful, please give it a like