CountDownLatch

CountDownLatch has only one constructor: CountDownLatch(int count)

Where count represents the number of the semaphore, and the specific implementation class is Sync, while Sycn inherits from AQS and implements several METHODS of AQS.

In the production environment, it can be used for the idea of divide and conquer, which means that some complex processing is divided into some sub-tasks, and the main program will not execute until all the processing tasks are finished. It can also be used for the process check of some tasks, for example, the main program can only obtain data and execute after all the checks are finished.

The concrete implementation of CountDownLatch ultimately utilizes a volatile variable state of AQS

countDown

The process of this method is to atomically set state to -1 through the CAS operation, and then determine whether the state after -1 becomes 0. If so, the doReleaseShared method is invoked to wake up a subsequent node.

As for doReleaseShared, it is AQS that is responsible for the implementation, so it is not explained here and will be explained later in the article

await

If state is already 0 when await is called, it will return directly and the current thread will not be suspended, whereas if state is not 0 the current thread will be blocked

If the thread has been set to interrupt when this method is called, InterruptedException is thrown directly

Realize the principle of

CountDownLatch is implemented by overriding tryAcquireShared and tryReleaseShared. After calling await method, if state > 0, Will call AQS doAcquireSharedInterruptibly inside method

This method first wraps the current thread as a node and initializes a FIFO queue. If only one thread calls the await method, then the addWaiter method forms a queue with only two nodes

Head -> curr (current thread)

Going back to the first figure, when the queue is formed, tryAcquireShared is called to determine whether the thread is allowed to execute

tryAcquireShared

This method should be called every time. If this method fails, the current thread should be enqueued and wait for another thread to wake up

As soon as the execution condition >0 is satisfied, the setHeadAndPropagate function is executed

The propagate variable is the one that is returned by the tryAcquireShared method, so as long as the thread is > 0, The current node is considered finished and subsequent nodes can be woken up

In this method, if only one thread is calling the await method, then the first thread is actually H == tail and h == head, so you don’t need to wake anyone up and just return

If multiple threads are calling the await method, then there is node after the node and we need to wake up the node (Thread Thread, node mode) constructor. So the ws value is 0, and then a CAS operation is performed to change the ws state to PROPAGATE

The general meanings of the above state values are as follows:

  • 1: Cancelled
  • 2: The thread needs to wake up
  • 3: Thread in conditional queue
  • 4: Notifies other shared nodes when releasing resources

Going back to the previous figure, if tryAcquireShared returns <0, all that remains is to change the node’s ws to -1, then suspend itself through lockSupport.park (this), and wait to wake up

doubt

There may be some doubt here, since the countDown method is to judge head! =null && head == tail to wake up the next node. If at the same time countDown changes state to 0 and the following if judgment is entered, will another thread (thread 2) be suspended?

No, the head and tail nodes are volatile, so visibility is guaranteed. If the current state is tryAcquireShared, then return. So it doesn’t block.

TryAcquireShared state = 0, state = 0, state = 0, state = 0, state = 0, state = 0 The countDown thread breaks.

If head does not equal tail, then the queue status is as follows:

head -> node

Also, thread 2 ultimately executes the doReleaseShared method, which is returned via head == h, so there is no thread-safety issue