JUC并发编程 Semaphore、CyclicBarrier、CountDownLatch

17 阅读18分钟

Java并发编程三大法器:Semaphore、CyclicBarrier、CountDownLatch

在Java并发编程中,有三个非常重要的同步工具类,它们被称为"并发控制三大法器":Semaphore(信号量)、CyclicBarrier(循环屏障)和CountDownLatch(倒计时门闩)。这三个类都位于java.util.concurrent包中,为多线程协调提供了强大的支持。

1. Semaphore(信号量)

1.1 概念介绍

Semaphore是一个计数信号量,从概念上讲,信号量维护了一组许可证(permits)。每个acquire()方法会阻塞,直到有许可证可用,然后获取该许可证。每个release()方法会添加一个许可证,可能会释放一个正在阻塞的获取者。

核心特点:

  • 控制同时访问特定资源的线程数量
  • 支持公平和非公平模式
  • 可以一次获取/释放多个许可证
  • 没有所有权概念,任何线程都可以释放许可证

1.2 使用示例

示例1:限流控制
public class RateLimiter {
    private final Semaphore semaphore;
    
    public RateLimiter(int maxConcurrent) {
        this.semaphore = new Semaphore(maxConcurrent);
    }
    
    public void execute(Runnable task) {
        try {
            semaphore.acquire();
            task.run();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            semaphore.release();
        }
    }
}

1.3 源码深度分析

1.3.1 核心架构设计

Semaphore基于AbstractQueuedSynchronizer(AQS)实现,采用共享模式来管理许可证。其核心设计包含以下几个关键组件:

/**
 * Semaphore的核心同步器抽象类
 * 继承自AQS,使用state字段存储许可证数量
 */
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 1192457210091910933L;

    /**
     * 构造函数:初始化许可证数量
     * @param permits 初始许可证数量,存储在AQS的state字段中
     */
    Sync(int permits) {
        setState(permits);  // 将许可证数量设置到AQS的state字段
    }

    /**
     * 获取当前可用许可证数量
     * @return 当前state值,即可用许可证数量
     */
    final int getPermits() {
        return getState();
    }

    /**
     * 非公平模式下尝试获取共享锁(许可证)
     * 这是Semaphore性能优化的关键方法
     * @param acquires 要获取的许可证数量
     * @return 剩余许可证数量,负数表示获取失败
     */
    final int nonfairTryAcquireShared(int acquires) {
        for (;;) {  // 自旋CAS操作,保证原子性
            int available = getState();  // 获取当前可用许可证数量
            int remaining = available - acquires;  // 计算获取后剩余数量
            
            // 关键判断:如果剩余数量<0(许可证不足)或者CAS更新成功,则返回
            if (remaining < 0 ||  // 许可证不足,直接返回负数
                compareAndSetState(available, remaining))  // CAS更新state
                return remaining;  // 返回剩余许可证数量
            // 如果CAS失败,继续自旋重试
        }
    }

    /**
     * 释放共享锁(归还许可证)
     * @param releases 要释放的许可证数量
     * @return true表示释放成功,会唤醒等待线程
     */
    protected final boolean tryReleaseShared(int releases) {
        for (;;) {  // 自旋CAS操作
            int current = getState();  // 获取当前许可证数量
            int next = current + releases;  // 计算释放后的数量
            
            // 溢出检查:防止许可证数量超过Integer.MAX_VALUE
            if (next < current) 
                throw new Error("Maximum permit count exceeded");
                
            // CAS更新state,成功则返回true
            if (compareAndSetState(current, next))
                return true;  // 返回true会触发AQS唤醒等待线程
        }
    }

    /**
     * 减少许可证数量(内部方法)
     * @param reductions 要减少的数量
     */
    final void reducePermits(int reductions) {
        for (;;) {
            int current = getState();
            int next = current - reductions;
            if (next > current) // 下溢检查
                throw new Error("Permit count underflow");
            if (compareAndSetState(current, next))
                return;
        }
    }

    /**
     * 清空所有许可证(内部方法)
     * @return 清空前的许可证数量
     */
    final int drainPermits() {
        for (;;) {
            int current = getState();
            if (current == 0 || compareAndSetState(current, 0))
                return current;
        }
    }
}
1.3.2 公平与非公平实现对比

非公平同步器(NonfairSync):

/**
 * 非公平同步器:性能优先,允许"插队"
 * 新来的线程可能比等待队列中的线程更早获得许可证
 */
static final class NonfairSync extends Sync {
    NonfairSync(int permits) {
        super(permits);
    }

    /**
     * 直接调用非公平获取方法,不检查等待队列
     */
    protected int tryAcquireShared(int acquires) {
        return nonfairTryAcquireShared(acquires);
    }
}

公平同步器(FairSync):

/**
 * 公平同步器:严格按照FIFO顺序分配许可证
 * 防止线程饥饿,但性能略低于非公平模式
 */
static final class FairSync extends Sync {
    FairSync(int permits) {
        super(permits);
    }

    /**
     * 公平模式下的获取方法
     * 必须先检查是否有前驱节点在等待
     */
    protected int tryAcquireShared(int acquires) {
        for (;;) {
            // 关键:先检查是否有前驱节点在等待
            if (hasQueuedPredecessors())  // AQS提供的方法,检查等待队列
                return -1;  // 有前驱节点,获取失败,当前线程需要排队
                
            // 没有前驱节点,尝试获取许可证(与非公平模式相同)
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }
}
1.3.3 核心方法调用链路分析

acquire()方法完整调用链:

// 1. 用户调用acquire()
public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);  // 委托给AQS的共享模式获取方法
}

// 2. AQS的acquireSharedInterruptibly方法
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
    if (Thread.interrupted())  // 检查中断状态
        throw new InterruptedException();
        
    // 尝试获取共享锁,返回值>=0表示成功
    if (tryAcquireShared(arg) < 0)  // 调用Semaphore重写的tryAcquireShared
        doAcquireSharedInterruptibly(arg);  // 获取失败,进入等待队列
}

// 3. Semaphore重写的tryAcquireShared(以非公平为例)
protected int tryAcquireShared(int acquires) {
    return nonfairTryAcquireShared(acquires);  // 调用非公平获取方法
}

// 4. 如果获取失败,AQS的doAcquireSharedInterruptibly方法
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;
                    failed = false;
                    return;
                }
            }
            // 检查是否需要阻塞,并处理中断
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

release()方法完整调用链:

// 1. 用户调用release()
public void release() {
    sync.releaseShared(1);  // 委托给AQS的共享模式释放方法
}

// 2. AQS的releaseShared方法
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {  // 调用Semaphore重写的tryReleaseShared
        doReleaseShared();  // 释放成功,唤醒等待线程
        return true;
    }
    return false;
}

// 3. Semaphore重写的tryReleaseShared
protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) 
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;  // 返回true触发唤醒操作
    }
}

// 4. AQS的doReleaseShared方法
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {  // 需要唤醒后继节点
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;
                unparkSuccessor(h);  // 唤醒后继节点
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;
        }
        if (h == head)  // 头节点没有变化,退出循环
            break;
    }
}
1.3.4 Semaphore工作流程图
flowchart TD
    A["Thread calls acquire()"] --> B{"Check interrupt status"}
    B -->|"Interrupted"| C["Throw InterruptedException"]
    B -->|"Not interrupted"| D["Call tryAcquireShared()"]
    
    D --> E{"Fair mode?"}
    E -->|"Yes"| F{"Has queued predecessors?"}
    E -->|"No"| G["Try acquire permits directly"]
    
    F -->|"Yes"| H["Return -1, acquire failed"]
    F -->|"No"| G
    
    G --> I{"Permits available?"}
    I -->|"Yes"| J["CAS update state"]
    I -->|"No"| H
    
    J --> K{"CAS success?"}
    K -->|"Yes"| L["Acquire success, return remaining"]
    K -->|"No"| G
    
    H --> M["Add to wait queue"]
    M --> N["Spin wait or block"]
    N --> O{"Woken up?"}
    O -->|"Yes"| D
    O -->|"No"| P{"Check interrupt?"}
    P -->|"Yes"| C
    P -->|"No"| N
    
    L --> Q["Method returns"]
    
    R["Thread calls release()"] --> S["Call tryReleaseShared()"]
    S --> T["CAS increment state"]
    T --> U{"CAS success?"}
    U -->|"Yes"| V["Call doReleaseShared()"]
    U -->|"No"| T
    
    V --> W["Wake up waiting threads"]
    W --> X["release() returns"]
    
    style A fill:#e1f5fe
    style R fill:#e8f5e8
    style C fill:#ffebee
    style Q fill:#f3e5f5
    style X fill:#f3e5f5

2. CyclicBarrier(循环屏障)

2.1 概念介绍

CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点。屏障被称为"循环"的,因为它可以在等待的线程被释放之后重新使用。

核心特点:

  • 让一组线程到达屏障点时被阻塞,直到最后一个线程到达
  • 支持屏障动作(barrier action),在所有线程到达后执行
  • 可以重复使用(循环性)
  • 支持超时和中断

2.2 使用示例

示例1:并行计算
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.BrokenBarrierException;

public class ParallelCalculator {
    private final int threadCount;
    private final CyclicBarrier barrier;
    private final double[][] matrix;
    private final double[] result;
    
    public ParallelCalculator(double[][] matrix) {
        this.matrix = matrix;
        this.threadCount = matrix.length;
        this.result = new double[threadCount];
        
        // 屏障动作:合并结果
        Runnable barrierAction = () -> {
            System.out.println("所有线程计算完成,开始合并结果");
            double sum = 0;
            for (double value : result) {
                sum += value;
            }
            System.out.println("最终结果: " + sum);
        };
        
        this.barrier = new CyclicBarrier(threadCount, barrierAction);
    }
    
    public void calculate() {
        for (int i = 0; i < threadCount; i++) {
            final int row = i;
            new Thread(() -> {
                try {
                    // 计算当前行
                    double sum = 0;
                    for (double value : matrix[row]) {
                        sum += value;
                    }
                    result[row] = sum;
                    
                    System.out.println("线程 " + row + " 计算完成");
                    
                    // 等待其他线程
                    barrier.await();
                    
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
示例2:多阶段任务
public class MultiPhaseTask {
    private final CyclicBarrier barrier;
    private final int workerCount;
    
    public MultiPhaseTask(int workerCount) {
        this.workerCount = workerCount;
        this.barrier = new CyclicBarrier(workerCount, () -> {
            System.out.println("阶段完成,进入下一阶段");
        });
    }
    
    public void start() {
        for (int i = 0; i < workerCount; i++) {
            final int workerId = i;
            new Thread(() -> {
                try {
                    for (int phase = 1; phase <= 3; phase++) {
                        System.out.println("Worker " + workerId + " 执行阶段 " + phase);
                        Thread.sleep(1000); // 模拟工作
                        
                        barrier.await(); // 等待其他线程完成当前阶段
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

2.3 源码深度分析

2.3.1 核心架构设计

CyclicBarrier不同于Semaphore和CountDownLatch,它没有使用AQS,而是基于ReentrantLock和Condition实现。这种设计更适合循环使用的场景:

/**
 * CyclicBarrier的核心实现
 * 使用ReentrantLock保证线程安全,Condition实现线程等待/唤醒
 */
public class CyclicBarrier {
    /** 用于保护屏障状态的锁 */
    private final ReentrantLock lock = new ReentrantLock();
    
    /** 等待条件:所有线程到达屏障点 */
    private final Condition trip = lock.newCondition();
    
    /** 参与屏障的线程总数(不变) */
    private final int parties;
    
    /** 屏障触发时执行的任务(可选) */
    private final Runnable barrierCommand;
    
    /** 当前代,用于实现循环功能 */
    private Generation generation = new Generation();
    
    /** 当前还在等待的线程数(递减计数器) */
    private int count;
    
    /**
     * 代(Generation)的概念:
     * 每次屏障被触发后,都会创建新的Generation
     * 这样可以区分不同轮次的屏障使用,实现循环功能
     */
    private static class Generation {
        /** 屏障是否被破坏 */
        boolean broken = false;
    }
    
    /**
     * 构造函数
     * @param parties 参与线程数
     * @param barrierAction 屏障触发时执行的动作
     */
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;  // 初始化等待计数
        this.barrierCommand = barrierAction;
    }
}
2.3.2 核心等待方法详解

dowait方法是CyclicBarrier的核心,处理所有等待逻辑:

/**
 * 核心等待方法:处理线程在屏障点的等待逻辑
 * @param timed 是否有超时限制
 * @param nanos 超时时间(纳秒)
 * @return 线程在屏障中的索引(最后到达的线程返回0)
 */
private int dowait(boolean timed, long nanos) 
    throws InterruptedException, BrokenBarrierException, TimeoutException {
    
    final ReentrantLock lock = this.lock;
    lock.lock();  // 获取锁,保证线程安全
    try {
        final Generation g = generation;  // 记录当前代,用于后续检查
        
        // 1. 检查屏障是否已被破坏
        if (g.broken)
            throw new BrokenBarrierException();
            
        // 2. 检查线程中断状态
        if (Thread.interrupted()) {
            breakBarrier();  // 破坏屏障
            throw new InterruptedException();
        }
        
        // 3. 递减等待计数,获取当前线程的索引
        int index = --count;
        
        // 4. 检查是否是最后一个到达的线程
        if (index == 0) {  // 最后一个线程到达,触发屏障
            boolean ranAction = false;
            try {
                // 执行屏障动作(如果有的话)
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                
                // 开启下一代,唤醒所有等待线程
                nextGeneration();
                return 0;  // 最后到达的线程返回0
            } finally {
                // 如果屏障动作执行失败,破坏屏障
                if (!ranAction)
                    breakBarrier();
            }
        }
        
        // 5. 不是最后一个线程,需要等待
        for (;;) {  // 无限循环等待
            try {
                if (!timed)  // 无超时等待
                    trip.await();
                else if (nanos > 0L)  // 有超时等待
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                // 处理等待过程中的中断
                if (g == generation && !g.broken) {
                    breakBarrier();  // 破坏屏障
                    throw ie;
                } else {
                    // 如果是其他代或屏障已破坏,恢复中断状态
                    Thread.currentThread().interrupt();
                }
            }
            
            // 6. 被唤醒后的检查
            if (g.broken)  // 屏障被破坏
                throw new BrokenBarrierException();
                
            if (g != generation)  // 进入了新的代,说明屏障已触发
                return index;
                
            if (timed && nanos <= 0L) {  // 超时
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock();  // 释放锁
    }
}
2.3.3 Generation机制详解

nextGeneration方法:开启新一代屏障

/**
 * 开启下一代屏障:实现循环使用的关键
 * 这个方法由最后到达屏障的线程调用
 */
private void nextGeneration() {
    // 1. 唤醒所有在当前代等待的线程
    trip.signalAll();
    
    // 2. 重置等待计数器,为下一轮使用做准备
    count = parties;
    
    // 3. 创建新的Generation对象,标志新一轮开始
    generation = new Generation();
}

breakBarrier方法:破坏屏障

/**
 * 破坏屏障:当发生异常或中断时调用
 * 会导致所有等待线程抛出BrokenBarrierException
 */
private void breakBarrier() {
    // 1. 标记当前代为已破坏
    generation.broken = true;
    
    // 2. 重置计数器
    count = parties;
    
    // 3. 唤醒所有等待线程(它们会检查到broken状态并抛出异常)
    trip.signalAll();
}
2.3.4 公共方法调用链路

await()方法调用链:

// 1. 用户调用await()
public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);  // 调用核心等待方法,无超时
    } catch (TimeoutException toe) {
        throw new Error(toe); // 不应该发生,因为没有设置超时
    }
}

// 2. 带超时的await方法
public int await(long timeout, TimeUnit unit) 
    throws InterruptedException, BrokenBarrierException, TimeoutException {
    return dowait(true, unit.toNanos(timeout));  // 调用核心等待方法,有超时
}

reset()方法:重置屏障

/**
 * 重置屏障到初始状态
 * 会破坏当前代,并创建新的代
 */
public void reset() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        breakBarrier();   // 先破坏当前代
        nextGeneration(); // 然后开启新代
    } finally {
        lock.unlock();
    }
}
2.3.5 CyclicBarrier工作流程图
flowchart TD
    A[线程调用await] --> B[获取锁]
    B --> C{屏障已破坏?}
    C -->|是| D[抛出BrokenBarrierException]
    C -->|否| E{线程被中断?}
    
    E -->|是| F[破坏屏障]
    F --> G[抛出InterruptedException]
    E -->|否| H[count递减]
    
    H --> I{count == 0?}
    I -->|是| J[最后一个线程到达]
    I -->|否| K[等待其他线程]
    
    J --> L{有屏障动作?}
    L -->|是| M[执行屏障动作]
    L -->|否| N[开启下一代]
    
    M --> O{执行成功?}
    O -->|是| N
    O -->|否| F
    
    N --> P[唤醒所有等待线程]
    P --> Q[返回索引0]
    
    K --> R[调用condition.await]
    R --> S{被唤醒}
    S --> T{屏障破坏?}
    T -->|是| D
    T -->|否| U{代已变化?}
    
    U -->|是| V[屏障已触发]
    U -->|否| W{超时?}
    W -->|是| X[破坏屏障并抛出TimeoutException]
    W -->|否| R
    
    V --> Y[返回线程索引]
    
    style A fill:#e1f5fe
    style J fill:#e8f5e8
    style D fill:#ffebee
    style G fill:#ffebee
    style X fill:#ffebee
    style Q fill:#f3e5f5
    style Y fill:#f3e5f5
2.3.6 关键设计特点总结
  1. Generation机制:通过Generation对象区分不同轮次,实现真正的循环使用
  2. 递减计数:每个线程到达时count减1,最后一个线程(count=0)负责触发屏障
  3. 异常传播:任何线程的异常都会破坏整个屏障,确保所有线程状态一致
  4. 锁机制:使用ReentrantLock而非AQS,更适合复杂的状态管理
  5. 条件等待:使用Condition实现线程的精确等待和唤醒

3. CountDownLatch(倒计时门闩)

3.1 概念介绍

CountDownLatch是一个同步辅助类,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。CountDownLatch用给定的计数初始化,await方法阻塞,直到由于countDown方法的调用而使当前计数达到零。

核心特点:

  • 一次性使用,计数不能重置
  • 一个或多个线程等待,直到其他线程完成操作
  • 支持超时等待
  • 计数为0后,所有等待线程立即释放

3.2 使用示例

示例1:等待多个服务启动
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class ServiceManager {
    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;
    private final int serviceCount;
    
    public ServiceManager(int serviceCount) {
        this.serviceCount = serviceCount;
        this.startSignal = new CountDownLatch(1);  // 启动信号
        this.doneSignal = new CountDownLatch(serviceCount);  // 完成信号
    }
    
    public void startServices() throws InterruptedException {
        // 创建并启动服务线程
        for (int i = 0; i < serviceCount; i++) {
            final int serviceId = i;
            new Thread(() -> {
                try {
                    System.out.println("服务 " + serviceId + " 准备就绪,等待启动信号");
                    startSignal.await();  // 等待启动信号
                    
                    System.out.println("服务 " + serviceId + " 开始启动");
                    Thread.sleep(2000);  // 模拟启动时间
                    System.out.println("服务 " + serviceId + " 启动完成");
                    
                    doneSignal.countDown();  // 通知完成
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
        
        Thread.sleep(1000);  // 准备时间
        System.out.println("发送启动信号");
        startSignal.countDown();  // 发送启动信号
        
        System.out.println("等待所有服务启动完成...");
        doneSignal.await();  // 等待所有服务完成
        System.out.println("所有服务启动完成!");
    }
}
示例2:并行任务执行
public class ParallelTaskExecutor {
    public void executeParallelTasks(List<Runnable> tasks) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(tasks.size());
        
        for (Runnable task : tasks) {
            new Thread(() -> {
                try {
                    task.run();
                } finally {
                    latch.countDown();  // 任务完成,计数减1
                }
            }).start();
        }
        
        // 等待所有任务完成
        if (latch.await(30, TimeUnit.SECONDS)) {
            System.out.println("所有任务执行完成");
        } else {
            System.out.println("任务执行超时");
        }
    }
}

3.3 源码深度分析

3.3.1 核心架构设计

CountDownLatch基于AQS的共享模式实现,设计简洁而高效。与Semaphore不同,它是一次性使用的同步工具:

/**
 * CountDownLatch的核心实现
 * 基于AQS共享模式,使用state字段作为倒计时计数器
 */
public class CountDownLatch {
    /**
     * CountDownLatch的同步器实现
     * 继承AQS,重写共享模式的获取和释放方法
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        /**
         * 构造函数:初始化计数值
         * @param count 初始计数,存储在AQS的state字段中
         */
        Sync(int count) {
            setState(count);  // 将计数值设置到AQS的state字段
        }

        /**
         * 获取当前计数值
         * @return 当前state值,即剩余计数
         */
        int getCount() {
            return getState();
        }

        /**
         * 尝试获取共享锁(等待操作的核心)
         * 这是AQS共享模式的关键方法
         * @param acquires 获取参数(对CountDownLatch无意义,通常为1)
         * @return 1表示获取成功,-1表示需要等待
         */
        protected int tryAcquireShared(int acquires) {
            // 关键逻辑:只有当计数为0时才能获取成功
            // 这实现了"等待计数归零"的语义
            return (getState() == 0) ? 1 : -1;
        }

        /**
         * 尝试释放共享锁(倒计时操作的核心)
         * @param releases 释放参数(通常为1,表示计数减1)
         * @return true表示释放成功且应该唤醒等待线程
         */
        protected boolean tryReleaseShared(int releases) {
            // 使用CAS循环确保原子性
            for (;;) {
                int c = getState();  // 获取当前计数
                
                // 如果计数已经为0,无需再减(防止负数)
                if (c == 0)
                    return false;
                    
                // 计算新的计数值
                int nextc = c - 1;
                
                // CAS更新计数值
                if (compareAndSetState(c, nextc))
                    // 关键:只有当计数减到0时才返回true
                    // 返回true会触发AQS唤醒所有等待线程
                    return nextc == 0;
            }
        }
    }

    /** 同步器实例 */
    private final Sync sync;

    /**
     * 构造函数
     * @param count 初始计数值,必须非负
     */
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
}
3.3.2 核心方法调用链路分析

await()方法完整调用链:

/**
 * 等待计数归零的方法调用链
 */

// 1. 用户调用await()
public void await() throws InterruptedException {
    // 委托给AQS的可中断共享获取方法
    sync.acquireSharedInterruptibly(1);
}

// 2. AQS的acquireSharedInterruptibly方法
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
    // 检查中断状态
    if (Thread.interrupted())
        throw new InterruptedException();
        
    // 尝试获取共享锁
    if (tryAcquireShared(arg) < 0)  // 调用CountDownLatch重写的方法
        doAcquireSharedInterruptibly(arg);  // 获取失败,进入等待队列
}

// 3. CountDownLatch重写的tryAcquireShared
protected int tryAcquireShared(int acquires) {
    // 简单而关键的逻辑:计数为0时返回1(成功),否则返回-1(失败)
    return (getState() == 0) ? 1 : -1;
}

// 4. 如果获取失败,AQS的doAcquireSharedInterruptibly方法
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;
                    failed = false;
                    return;
                }
            }
            // 检查是否需要阻塞,并处理中断
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

countDown()方法完整调用链:

/**
 * 倒计时方法调用链
 */

// 1. 用户调用countDown()
public void countDown() {
    // 委托给AQS的共享释放方法
    sync.releaseShared(1);
}

// 2. AQS的releaseShared方法
public final boolean releaseShared(int arg) {
    // 尝试释放共享锁
    if (tryReleaseShared(arg)) {  // 调用CountDownLatch重写的方法
        doReleaseShared();  // 释放成功,唤醒等待线程
        return true;
    }
    return false;
}

// 3. CountDownLatch重写的tryReleaseShared
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();  // 获取当前计数
        if (c == 0)
            return false;  // 已经为0,无需释放
        int nextc = c - 1;  // 计数减1
        if (compareAndSetState(c, nextc))
            return nextc == 0;  // 只有减到0时才返回true,触发唤醒
    }
}

// 4. 如果返回true,AQS的doReleaseShared方法
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {  // 需要唤醒后继节点
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;
                unparkSuccessor(h);  // 唤醒后继节点
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;
        }
        if (h == head)  // 头节点没有变化,退出循环
            break;
    }
}
3.3.3 带超时的等待方法
/**
 * 带超时的等待方法
 * @param timeout 超时时间
 * @param unit 时间单位
 * @return true表示在超时前计数归零,false表示超时
 */
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
    // 委托给AQS的带超时共享获取方法
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

// AQS的tryAcquireSharedNanos方法
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // 先尝试快速获取,失败则进入带超时的等待
    return tryAcquireShared(arg) >= 0 ||
           doAcquireSharedNanos(arg, nanosTimeout);
}
3.3.4 CountDownLatch工作流程图
flowchart TD
    A["Thread calls await()"] --> B{"Check interrupt status"}
    B -->|"Interrupted"| C["Throw InterruptedException"]
    B -->|"Not interrupted"| D["Call tryAcquireShared()"]
    
    D --> E{"Count equals 0?"}
    E -->|"Yes"| F["Acquire success, method returns"]
    E -->|"No"| G["Add to wait queue"]
    
    G --> H["Spin wait or block"]
    H --> I{"Woken up?"}
    I -->|"Yes"| D
    I -->|"No"| J{"Check interrupt?"}
    J -->|"Yes"| C
    J -->|"No"| H
    
    K["Thread calls countDown()"] --> L["Call tryReleaseShared()"]
    L --> M{"Current count is 0?"}
    M -->|"Yes"| N["No operation, return false"]
    M -->|"No"| O["CAS decrement count"]
    
    O --> P{"CAS success?"}
    P -->|"No"| O
    P -->|"Yes"| Q{"Count decremented to 0?"}
    
    Q -->|"No"| R["Return false, no wake up"]
    Q -->|"Yes"| S["Return true, trigger wake up"]
    
    S --> T["Call doReleaseShared()"]
    T --> U["Wake up all waiting threads"]
    U --> V["countDown() returns"]
    
    style A fill:#e1f5fe
    style K fill:#e8f5e8
    style C fill:#ffebee
    style F fill:#f3e5f5
    style V fill:#f3e5f5
    style S fill:#fff3e0
3.3.5 关键特性分析

1. 一次性使用特性:

// CountDownLatch的计数只能递减,不能重置
// 一旦计数归零,所有后续的await()调用都会立即返回
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
    // 如果计数已经为0,tryAcquireShared会立即返回1
    // 不会进入等待队列
}

2. 共享模式的传播特性:

// 当计数归零时,AQS会唤醒所有等待线程
// 这是通过共享模式的传播机制实现的
private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head;
    setHead(node);
    // propagate > 0 表示还有资源可用,继续传播唤醒
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();  // 继续唤醒后续节点
    }
}

3. 线程安全保证:

// 使用CAS操作保证计数递减的原子性
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();
        if (c == 0) return false;
        int nextc = c - 1;
        // CAS确保在并发环境下的正确性
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
3.3.6 与其他同步工具的对比
特性CountDownLatchCyclicBarrierSemaphore
AQS模式共享模式不使用AQS共享模式
计数方向只能递减固定循环可增可减
重用性一次性可循环可重复
等待语义等待计数归零等待所有线程到达等待许可证
唤醒机制一次性唤醒所有循环唤醒所有按需唤醒

4. 三者对比总结

特性SemaphoreCyclicBarrierCountDownLatch
用途控制资源访问数量多线程同步到达屏障点等待多个操作完成
重用性可重复使用可循环使用一次性使用
等待方式获取许可证所有线程互相等待一个/多个线程等待
触发条件有可用许可证所有线程到达屏障计数归零
计数方向可增可减固定数量循环只能递减
异常处理支持中断支持中断和超时支持中断和超时
典型场景限流、资源池分阶段并行计算服务启动、任务完成等待

5. 最佳实践

5.1 选择原则

  • Semaphore:当需要控制同时访问某个资源的线程数量时使用
  • CyclicBarrier:当多个线程需要在某个点同步,并且需要重复这个过程时使用
  • CountDownLatch:当需要等待多个操作完成后再继续执行时使用

5.2 注意事项

  1. 资源释放:使用Semaphore时要确保在finally块中释放许可证
  2. 异常处理:CyclicBarrier和CountDownLatch都要妥善处理中断异常
  3. 超时设置:在生产环境中建议使用带超时的等待方法
  4. 线程安全:这些类本身是线程安全的,但使用它们的业务逻辑需要考虑线程安全

5.3 性能考虑

  • Semaphore支持公平和非公平模式,非公平模式性能更好但可能导致线程饥饿
  • CyclicBarrier基于ReentrantLock实现,在高并发场景下性能可能不如基于CAS的实现
  • CountDownLatch基于AQS的共享模式,性能较好且适合一次性场景

通过深入理解这三个并发控制工具的原理和使用场景,我们可以在实际开发中选择最合适的工具来解决并发协调问题,编写出更加健壮和高效的多线程程序。