Java concurrency series [7] ---- CountDownLatch source code analysis,

Source: Internet
Author: User

Java concurrency series [7] ---- CountDownLatch source code analysis,

CountDownLatch is a useful tool class. With it, we can intercept one or more threads so that they can be executed after a certain condition is ripe. It provides a counter internally. When constructing a lock, you must specify the initial value of the counter 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. Every time the countDown method is called, the counter will be reduced by 1 until the counter value is reduced to 0, it indicates that the condition is mature, all threads blocked by calling the await method will be awakened. This is the internal mechanism of CountDownLatch. It seems very simple. It is nothing more than blocking some threads to execute them after reaching a certain condition. However, CountDownLatch has a wide range of application scenarios, so you can use it to make full use of it. The most common application scenario is to enable multiple threads to execute a task at the same time, and then count the summary results after all the tasks are completed. Dynamically demonstrates the entire process of locking and blocking threads.

It is demonstrated that five threads are blocked by calling the await method. They need to wait until the counter value is reduced to 0 to continue execution. The initial value of the counter is specified when the lock is constructed, and the value is reduced by 1 with the call of the countDown method each time. The following code shows the CountDownLatch constructor.

1 // constructor 2 public CountDownLatch (int count) {3 if (count <0) throw new IllegalArgumentException ("count <0"); 4 this. sync = new Sync (count); 5}

CountDownLatch has only one constructor with parameters. a value greater than 0 must be input as the initial value of the counter. Otherwise, an error is returned. We can see that in the constructor, only a new Sync object is removed and assigned to the member variable sync. Like other synchronization tools, CountDownLatch depends on AQS, which is an application in AQS sharing 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. Next, let's take a look at the code of the Sync internal class.

1 // synchronization2 private static final class Sync extends actqueuedsynchronizer {3 4 // constructor 5 Sync (int count) {6 setState (count ); 7} 8 9 // get the current synchronization Status 10 int getCount () {11 return getState (); 12} 13 14 // try to get the lock 15 // return a negative number: indicates that the current thread failed to get 16 // return zero value: indicates that the current thread has succeeded in obtaining the data, but the successor thread cannot get 17 // return a positive number: indicates that the current thread has succeeded in obtaining the data, and the successor thread can also get the successful 18 protected int tryAcquireShared (int acquires) {19 return (getState () = 0 )? 1:-1; 20} 21 22 // try to release Lock 23 protected boolean tryReleaseShared (int releases) {24 (;;) {25 // obtain the synchronization status 26 int c = getState (); 27 // if the synchronization status is 0, 28 if (c = 0) cannot be released) {29 return false; 30} 31 // otherwise, the synchronization status will be reduced by 132 int nextc = C-1; 33 // use CAS to update the synchronization status 34 if (compareAndSetState (c, nextc) {35 return nextc = 0; 36} 37} 38} 39}

We can see that the Sync constructor sets the synchronization status value to the passed parameter value. Each time the countDown method is called, the value of the synchronization state is reduced by 1, which is the implementation principle of the counter. The await and countDown methods are the most common methods when CountDownLatch is used. Calling the await method will block the current thread until the counter is 0. Calling the countDown method will reduce the counter value by 1 until it is reduced to 0. Next, let's take a look at how the await method is called.

1 // cause the current thread to wait until the bolt is reduced to 0, or the thread is interrupted. 2 public void await () throws InterruptedException {3 // obtain 4 sync in response to thread interruption. acquireSharedInterruptibly (1); 5} 6 7 // obtain the lock in the break mode (share mode) 8 public final void acquireSharedInterruptibly (int arg) throws InterruptedException {9 // first checks whether the Thread is interrupted. if yes, an exception 10 if (Thread. interrupted () {11 throw new InterruptedException (); 12} 13 // 1. try to get the lock 14 if (tryAcquireShared (arg) <0) {15 // 2. if the retrieval fails, enter the Method 16 doAcquireSharedInterruptibly (arg); 17} 18}

When the thread calls the await method, it actually calls the acquireSharedInterruptibly method of AQS. This method obtains the lock in response to thread interruption, and the code of this method is also attached. We can see that the acquireSharedInterruptibly method first calls the tryAcquireShared method to try to obtain the lock. We can see the logic of the tryAcquireShared method rewritten in Sync. The implementation logic of the method is very simple, that is, to determine whether the current synchronization status is 0. If it is 0, 1 is returned, indicating that the lock can be obtained, otherwise, "-1" indicates that the lock cannot be obtained. If the tryAcquireShared method returns 1, the thread can continue execution without waiting. If-1 is returned, the doAcquireSharedInterruptibly method will be called later to let the thread enter the synchronization queue and wait. This is the principle that calling the await method will block the current thread. Let's take a look at how the countDown method wakes up the blocked thread.

1 // method 2 to reduce the bolt public void countDown () {3 sync. releaseShared (1); 4} 5 6 // unlock operation (share mode) 7 public final boolean releaseShared (int arg) {8 // 1. try to release the lock 9 if (tryReleaseShared (arg) {10 // 2. if the release succeeds, it will wake up other threads 11 doReleaseShared (); 12 return true; 13} 14 return false; 15}

We can see that the countDown method calls the releaseShared method, which is also the method in AQS. We have also posted its code on it. In the releaseShared method, the first method is to call the tryReleaseShared method to try to release the lock. The tryReleaseShared method is an abstract method in AQS, and its implementation logic is in the sub-class Sync class, we can find this method in the Sync code posted above. TryReleaseShared: If the return value is true, the release is successful. If the return value is false, the release fails. true is returned only when the synchronization status is changed to 0 after the synchronization status is reduced by 1, in other cases, false is returned. When tryReleaseShared returns true, it immediately calls the doReleaseShared method to wake up all threads in the synchronization queue. This explains why all blocked threads will be awakened after the countDown method is called for the last time the counter is reduced to 0. The basic principles of CountDownLatch are roughly the same. Let's take a look at its example.

Application Scenario: You must wait for the three players to complete the licensing process when playing the game.

1 public class Player extends Thread {2 3 private static int count = 1; 4 private final int id = count ++; 5 private CountDownLatch latch; 6 7 public Player (CountDownLatch latch) {8 this. latch = latch; 9} 10 11 @ Override12 public void run () {13 System. out. println ("[player" + id + "] admitted"); 14 latch. countDown (); 15} 16 17 public static void main (String [] args) throws InterruptedException {18 CountDownLatch latch = new CountDownLatch (3); 19 System. out. println ("starting from the Board, waiting for players to enter... "); 20 new Player (latch ). start (); 21 new Player (latch ). start (); 22 new Player (latch ). start (); 23 latch. await (); 24 System. out. println ("players have arrived. Start licensing... "); 25} 26 27}

The running result shows that the licensing operation must be performed only after all players enter the venue. Let's comment out latch. await () in 23 rows and compare them to see the results.

We can see that after the latch. await () line is commented out, it cannot be guaranteed that the licensing will start after all players enter the venue.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.