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 continuously tracked, and there is this code in AbstractQueuedSynchronizer:

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 Sync that implements AbstractQueuedSynchronizer. When initializing CountDownLatch, Sync is initialized, and a queue (linked list) is generated based on the size of CountDownLatch (size). Before the last one is left with the head, it will loop continuously (Await).

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 by 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, until countdown.

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 previous threads have finished, because count > 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.