banner
RustyNail

RustyNail

coder. 【blog】https://rustynail.me 【nostr】wss://ts.relays.world/ wss://relays.world/nostr

CountDownLatch

No one can move before I open the door.

CountDownLatch is a synchronization utility class in the Java Concurrent package. It allows one or more threads to wait for an event to occur.

CountDownLatch has a counter that represents the number of events to wait for, and an await method that waits as long as the counter is greater than zero.

There are generally two use cases: creating a group of threads to execute concurrently or waiting until all threads have completed their tasks. Of course, it is also useful for performing tasks in a specific order.

await method#

This method is tracked all the way to AbstractQueuedSynchronizer where this code is found:

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

In CountDownLatch, there is an inner class called Sync, which implements AbstractQueuedSynchronizer. When initializing CountDownLatch, Sync is also initialized, and a queue (linked list) is generated based on the size of CountDownLatch (size). It will loop (Await) until there is only one node left before the head.

In other words, when calling await, it tries to acquire a shared lock and fails because the node is not the head, so it is added to the tail (added using CAS).

1#
CountDownLatch startLatch = new CountDownLatch(1);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    startLatch.await();
                    System.out.println(Instant.now());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
System.out.println("start...");
startLatch.countDown();

All threads are stuck because the count is 1, and they will be released when countdown is called.

2#
CountDownLatch waitLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
    int finalI = i;
    new Thread(() -> {
        try {
            Thread.sleep(new Random().nextInt(2000));
            System.out.println("i: " + finalI);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            waitLatch.countDown();
        }
    }).start();
}
waitLatch.await();
System.out.println("end");

The output end is executed after all the previous threads have completed because the count is greater than 0. If the initial value of count is too small, the final end will be output prematurely.

i: 8
i: 3
i: 6
i: 5
i: 0
end
i: 4
i: 1
i: 9
i: 7
i: 2
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.