详解CountDownlatch

引言

CountDownLatch 是 Java 并发包 java.util.concurrent 中的一个同步工具类,由著名的并发编程大师 Doug Lea 所开发。它允许一个或多个线程等待其他线程完成操作后再继续执行,其核心思想是通过一个计数器,对计数器进行调度来实现

原理

CountDownLatch 初始化时会设置一个计数器的值,该值表示需要等待完成的操作数量。每当一个线程完成了自己的任务后,会调用 countDown() 方法将计数器减 1。当计数器的值变为 0 时,意味着所有需要等待的操作都已完成,此时在 await() 方法上等待的线程将被唤醒,继续执行后续操作。

源码分析

内部类

private final Sync sync;

private static final class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 4982264981922014374L;

    Sync(int count) {
        setState(count);
    }

    int getCount() {
        return getState();
    }

    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }

    protected boolean tryReleaseShared(int releases) {
        // Decrement count; signal when transition to zero
        for (;;) {
            int c = getState();
            if (c == 0)
                return false;
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }
}
  • sync:CountDownLatch 内部使用 Sync 类的实例,Sync 继自 AbstractQueuedSynchronizer(AQS),借助 AQS 的状态(state)来表示计数器的值。
  • Sync 类:
    • 构造函数 Sync(int count):在创建 Sync 实例时,会调用 setState(count) 方法将 AQS 的状态初始化为传入的计数器值。
    • getCount() 方法:返回 AQS 的当前状态,即计数器的当前值。
    • tryAcquireShared(int acquires) 方法:用于尝试以共享模式获取锁。当计数器值为 0 时返回 1,表示可以获取锁;否则返回 -1,表示无法获取锁。
    • tryReleaseShared(int releases) 方法:用于尝试以共享模式释放锁。使用 for(;;) 无限循环结合 compareAndSetState 方法(CAS 操作)来安全地将计数器减 1。当计数器减为 0 时返回 true,表示释放成功且可以唤醒等待线程;否则返回 false。

结合源码我们一般是主线程设计为等待线程,线程池去完成计数器的增减

内部方法

await()

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
  • await() 方法会调用 sync 的 acquireSharedInterruptibly(1) 方法,该方法是 AQS 提供的以共享模式获取锁的可中断方法。其具体逻辑为:
    • 检查当前线程是否被中断,若被中断则抛出 InterruptedException 异常。

    • 调用 tryAcquireShared 方法尝试获取锁,若计数器为 0 则获取成功,线程继续执行;否则将当前线程放入等待队列并阻塞。

await(long timeout, TimeUnit unit) 

public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

该方法允许线程等待指定的时间。调用 sync 的 tryAcquireSharedNanos(1, unit.toNanos(timeout)) 方法,在指定时间内尝试以共享模式获取锁。若在超时时间内计数器变为 0 则返回 true;若超时则返回 false。

countDown() 

countDown() 方法调用 sync 的 releaseShared(1) 方法,以共享模式释放锁。该方法会调用 tryReleaseShared 方法尝试将计数器减 1,若减为 0 则唤醒所有在等待队列中的线程。

实战

下面是多线程实战CountDownlatch,依旧是主线程等待,在循环中创建线程调用await方法

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个 CountDownLatch 实例,计数器初始值为 3
        CountDownLatch latch = new CountDownLatch(3);

        // 创建并启动三个工作线程
        for (int i = 0; i < 3; i++) {
            final int workerId = i;
            new Thread(() -> {
                try {
                    System.out.println("Worker " + workerId + " is working.");
                    // 模拟工作
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println("Worker " + workerId + " has finished.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 工作完成,计数器减 1
                    latch.countDown();
                }
            }).start();
        }

        // 主线程等待所有工作线程完成
        System.out.println("Main thread is waiting for workers to finish.");
        latch.await();
        System.out.println("All workers have finished. Main thread can continue.");
    }
}

你可能感兴趣的:(java,开发语言)