CountDownLatch is a useful tool class. Using it, we can intercept one or more threads to execute after a certain condition is ripe. Its internal content provides a counter, and the initial value of the counter must be specified when constructing a lock, and the initial value of the counter must be greater than 0. In addition, it also provides a countDown method to operate the counter value. The counter will be decremented by 1 every time the countDown method is called. When the counter value is reduced to 0, it means that the conditions are ripe, and all threads blocked by calling the await method will be awakened. This is the internal mechanism of CountDownLatch. It looks very simple, it is nothing more than blocking some threads and allowing them to execute after reaching a certain condition. However, CountDownLatch has a wide range of application scenarios. As long as you have a big mind and use it, you can play various tricks. The most common application scenario is to enable multiple threads to execute a task at the same time, and then count and summarize the results after all tasks are executed. The following figure dynamically demonstrates the entire process of blocking threads.
The above figure shows that 5 threads are blocked by calling the await method, and they need to wait for the counter value to decrease to 0 before continuing to execute. The initial value of the counter is specified when constructing a lock, and is subsequently reduced by 1 with each call to the countDown method. The following code posts the CountDownLatch construction method.
//Constructor public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }CountDownLatch has only one parameter constructor, and a value greater than 0 must be passed as the initial value of the counter, otherwise an error will be reported. You can see that in the constructor, just new a Sync object and assign it to the member variable sync. Like other synchronization tool classes, the implementation of CountDownLatch relies on AQS, which is an application in AQS shared mode. CountDownLatch implements an internal class Sync and uses it to inherit AQS, so that most of the methods provided by AQS can be used. Let's take a look at the code of Sync internal class.
//Synchronizer private static final class Sync extends AbstractQueuedSynchronizer { //Constructor Sync(int count) { setState(count); } //Get the current synchronization state int getCount() { return getState(); } //Try to obtain the lock//Return negative number: indicates that the current thread failed to obtain//Return zero value: indicates that the current thread has been acquired successfully, but the subsequent thread can no longer obtain //Return positive number: indicates that the current thread has been acquired successfully, and the subsequent thread can also obtain success protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; } //Try to release the lock protected boolean tryReleaseShared(int releases) { for (;;) { //Get the synchronization state int c = getState(); //If the synchronization state is 0, if (c == 0) { return false; } //Otherwise, reduce the synchronization state by 1 int nextc = c-1; //Use CAS method to update the synchronization state if (compareAndSetState(c, nextc)) { return nextc == 0; } } }}You can see that Sync's constructor will set the value of the synchronization state to the passed parameter value. After that, every time the countDown method is called, the value of the synchronous state will be reduced by 1, which is the implementation principle of the counter. The two most commonly used methods when using the CountDownLatch tool class are the await method and the countDown method. Calling the await method will block the current thread until the counter is 0, and calling the countDown method will decrement the counter value by 1 until it is reduced to 0. Let's take a look at how the await method is called.
//Cause the current thread to wait until the latch decreases to 0, or the thread is interrupted public void await() throws InterruptedException { //Acquiring sync.acquireSharedInterruptibly(1);}//Acquiring lock in interruptable mode (shared mode) public final void acquireSharedInterruptibly(int arg) throws InterruptedException { //First determine whether the thread is interrupted, if so, throw an exception if (Thread.interrupted()) { throw new InterruptedException(); } //1. Try to acquire the lock if (tryAcquireShared(arg) < 0) { //2. If the acquisition fails, enter the method doAcquireSharedInterruptibly(arg); }}When the thread calls the await method, it actually calls the acquireSharedInterruptibly method of AQS. This method acquires the lock in response to thread interruptions. The code for this method is also posted above. We can see that in the acquireSharedInterruptibly method, first, it will call the tryAcquireShared method to try to acquire the lock. We see the logic of the tryAcquireShared method rewritten in Sync. The implementation logic of the method is very simple, which is to judge whether the current synchronization state is 0. If it is 0, return 1 means that the lock can be acquired, otherwise return -1 means that the lock cannot be acquired. If the tryAcquireShared method returns 1, the thread can continue to execute without waiting. If -1 is returned, then the doAcquireSharedInterruptibly method will be called in the synchronous queue to wait. This is the principle that calling the await method will block the current thread. Let’s see how the countDown method wakes up the blocking thread.
// Method to reduce latch public void countDown() { sync.releaseShared(1);}//Release operation (shared mode) public final boolean releaseShared(int arg) { //1. Try to release the lock if (tryReleaseShared(arg)) { //2. If the release is successful, wake up other threads doReleaseShared(); return true; } return false;}You can see that the releaseShared method is called in the countDown method. This method is also a method in AQS. We also posted its code on it. The first thing in the releaseShared method is to call the tryReleaseShared method to try to release the lock. The tryReleaseShared method is an abstract method in AQS. Its specific implementation logic is in the subclass Sync class. We can find this method in the Sync class code posted above. If the tryReleaseShared method returns true to release, and returns false to release failure. It will only return true if the synchronization state is exactly 0 after decreasing 1. In other cases, false will be returned. Then when tryReleaseShared returns true, the doReleaseShared method will be called immediately to wake up all threads in the synchronization queue. This explains why the last time the countDown method is called to reduce the counter to 0 will wake up all blocked threads. These are the basic principles of CountDownLatch. Let’s take a look at an example of its use.
Application scenario: When playing Happy Landlord, you must wait for all three players to arrive before you can deal cards.
public class Player extends Thread { private static int count = 1; private final int id = count++; private CountDownLatch latch; public Player(CountDownLatch latch) { this.latch = latch; } @Override public void run() { System.out.println("【Player" + id + "] Entry"); latch.countDown(); } public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(3); System.out.println("The game starts, waiting for the player to enter..."); new Player(latch).start(); new Player(latch).start(); new Player(latch).start(); latch.await(); System.out.println("The players have arrived, start dealing..."); } }The operation results show that the dealing operation must be carried out after all players enter the field. We comment out the 23 lines latch.await() and compare it to see the results.
You can see that after commenting out the line latch.await(), it is not guaranteed that all players will start to deal cards only after entering the field.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.