计数器 CountDownLatch

CountDownLatch

Semaphore 更加适合用于控制对有限资源的访问,特别是当你需要允许一定数量的线程同时访问资源时

CountDownLatch 更加适合用于协调多个线程的完成状态,确保在某些操作完成后再执行后续操作

它用于协调多个线程的执行,使得某些操作必须等到其他操作完成后才能继续进行,常用方法如下

CountDownLatch latch = new CountDownLatch(3); // 创建一个计数器,计数器的值为 3(需要等待的线程数量或事件数量是 3)
latch.countDown(); // 减少计数器的值,每次调用都会将计数器 -1
latch.await(); // 在哪个线程调用 await,哪个线程就会阻塞,直到计数器为 0 才恢复运行

用法示例

import java.util.concurrent.CountDownLatch;

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

        // 启动3个线程,每个线程完成后调用countDown()
        for (int i = 0; i < 3; i++) {
            new Thread(new MyThread(latch)).start();
        }

        // 主线程等待,直到计数器减到零
        latch.await();

        System.out.println("All workers have finished their tasks.");
    }
}

class MyThread implements Runnable {
    private CountDownLatch latch;

    public Worker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            // 模拟任务处理
            Thread.sleep(1000);
            System.out.println("Worker finished");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 完成任务后调用 countDown
            latch.countDown();
        }
    }
}

原理

  • state 初始设置了值,当主线程调用 await() 发现 state 不是 0,获取不到锁,主线程就进入队列阻塞
  • 每调用一次 countDown() 方法,state -1,当减后是0,需要唤醒等待的线程
  • 需要注意的是,当 state 已经是 0,调用 countDown() 方法是无意义的

构建一个计数器

// 构造方法
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    // new 了一个 Sync,和前端的所有锁都是一样的,这个 Sync 继承了 AQS
    this.sync = new Sync(count);
}

// Sync 构造方法
Sync(int count) {
    setState(count); // 调用父类 AQS 的方法,修改 state 的值
}

// java.util.concurrent.locks.AbstractQueuedSynchronizer#setState
protected final void setState(int newState) {
    state = newState;
}

计数器-1

// java.util.concurrent.CountDownLatch#countDown
public void countDown() {
    sync.releaseShared(1); // AQS 的方法
}

// java.util.concurrent.locks.AbstractQueuedSynchronizer#releaseShared
// 1,返回值没意义,因为在 countDown 方法中没根据返回值做什么处理
// 2,tryReleaseShared() 方法返回 true:说明是 state - 1 后为 0,需要唤醒等待线程
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) { // 模板方法
        doReleaseShared(); // 唤醒等待线程
        return true;
    }
    return false;
}

// java.util.concurrent.locks.AbstractQueuedSynchronizer#tryReleaseShared
protected boolean tryReleaseShared(int arg) {
    throw new UnsupportedOperationException();
}

// java.util.concurrent.CountDownLatch.Sync#tryReleaseShared
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();
        if (c == 0) // 已经是 0,返回 false
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0; // 仅当 state - 1 后 state 是 0 才返回 true
    }
}

await() 方法

这个就更简单了

// java.util.concurrent.CountDownLatch#await()
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

// java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0) // 获取锁,只会返回 1 或 -1(-1 表示不能获取锁,要阻塞)
        doAcquireSharedInterruptibly(arg); // 前面分析过好多次了,进入队列,维护链表,挂起线程
}

// java.util.concurrent.CountDownLatch.Sync#tryAcquireShared
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1; // 当 state 不是 0,返回 -1
}
posted @   CyrusHuang  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示