banner
RustyNail

RustyNail

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

CountDownLatch

在我开门之前谁也别想动

CountDownLatch 是 Java Concurrent 包里边的一个同步工具类。它可以使一个或多个线程等待一个事件发生。

CountDownLatch 有一个计数器,表示等待的事件数量。以及一个 await 方法,当计数器一直大于零的时候,会一直等待。

一般有两种用法,创建一组线程,让他们并发的执行 或者 等待,直到所有的线程完成任务。当然用来做一些有先后顺序的工作也很好用。

await 方法#

一直追踪这个方法,在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);
    }
}

在 CountDownLatch 里有个内部类Sync,实现了AbstractQueuedSynchronizer, 在初始化 CountDownLatch 的时候,初始化 Sync,同时生成一个

队列(链表),根据 CountDownLatch(size)的 size,来生成 Node, 在最后一个就剩 head 之前会一直循环(Await)。

也就是说,调用 await 的时候,尝试获取共享锁,然后失败,因为节点不是 head, 所以会加入到 tail(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();

所有线程因为 count 是 1,被卡住,直到 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");

最后输出end被执行之前要先执行完之前的线程,因为 count>0, 如果 count 的初始值过小,就会提前输出最后的end

i: 8
i: 3
i: 6
i: 5
i: 0
end
i: 4
i: 1
i: 9
i: 7
i: 2
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。