Get started with CountDownLatch

CountDownLatch allows a thread to wait for multiple other threads to execute. This means that CountDownLatch can implement thread waits.

Realize the principle of

Countdownlatch internally defines a counter and a wait queue. Before the counter decays to 0, the wait queue is blocked, and when the counter reaches 0, it wakes up all the blocked threads in the wait queue

The main API

CountDownLatch(int count); // set count to await(); // Block the current thread and await it in the blocking queue (long timeout, TimeUnit unit); // Block the current thread and countDown() begins when the timeout expires; // The counter decreases by 1Copy the code

The implementation of the API

Create counter

  public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
Copy the code

Blocking threads

public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); / / reentrant lock number greater than 0, the new node to join the blocking queue, suspends the current thread if (tryAcquireShared (arg) < 0) doAcquireSharedInterruptibly (arg); }Copy the code

Timer decrement

Public Final Boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared(); public Final Boolean releaseShared(arg) {doReleaseShared(); // Wake up all blocked nodes in the queue return true; } return false; } private void doReleaseShared() {// Wake up all threads in the blocking queue for (;;) { Node h = head; if (h ! = null && h ! = tail) { int ws = h.waitStatus; If (ws == node.signal) {// Whether the Node is waiting to wake up if (! CompareAndSetWaitStatus (h, node.signal, 0))// Change the status to initial continue; unparkSuccessor(h); } else if (ws == 0 &&! compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; }}Copy the code

Usage scenarios

Countdownlatch can be used in business situations to trigger returned cases that need to wait for multiple operations to complete.

Take a practical example, taobao customer service side of the customer service workbench, after the user into the line, according to the order card information sent by the user, to show the customer service related user information, user portrait, historical after-sales and order goods details and other information, assist customer service to solve the problem of users. The information comes from different internal technology group, such as order information, for one, may be in use the order number for the order information of the service interface implementations, call order set of interfaces provide order subject information, call the commodity group interface for commodity information, call the warehouse group interface to get the order information warehouse the goods, Call the inventory group to obtain the current inventory information of the product and so on. In an interface, if the calls of one interface are written down, the calls of these interfaces will become serial. Most of the servitization interfaces are consumed in network transmission, so it is necessary to figure out how to parallelize the calls of these interfaces. In this case, countDownLatch can be used to obtain the results by invoking the servitization interfaces of each group in parallel. When all the servitization interfaces of each group return, the countDownLatch counter is triggered and the countDownLatch counter decreases to 0. At this point, the interface of the entire customer service workbench returns, providing the response time of the entire interface.

Code implementation

@Service @AllArgConstructor public class OrderFacade { private OrderCompose orderCompose; private ItemCompose itemCompose; private WarehouseCompose warehouseCompose; private StorageCompose storageCompose; public Order getOrderItemByOrderItemId(String orderId){ OrderResult result = null; Executor executor = Executors.newFixedThreadPool(4); CountDownLatch latch = new CountDownLatch(4); executor.execute(() -> { Order order = orderCompose.getOrder(orderId); latch.countDown(); }); executor.execute(() -> { Item item = itemCompose.getSku(orderId); latch.countDown(); }); executor.execute(() -> { Warehouse warehouse = warehouseCompose.getWarehouse(orderId); latch.countDown(); }); executor.execute(() -> { Storage storage = storageCompose.getStorage(orderId); latch.countDown(); }); latch.await(); result = buildOrder(order, item, warehouse, storage); return result; }}Copy the code