java并发-CountDownLatch和CyclicBarrier源码解析

本文深入解析了CountDownLatch和CyclicBarrier在Java并发编程中的作用和实现细节。CountDownLatch作为计数器,用于同步多个线程,直到计数器归零才释放所有等待线程;而CyclicBarrier则确保所有线程到达指定点后,同时继续执行,如同赛跑的起跑枪。两者均基于AQS框架,但应用场景和机制有所不同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CountDownLatch和CyclicBarrier

在看Curator的时候,看到了分布式屏障的设计,所以回头仔细看看Jdk的CyclicBarrier实现思路。这里CountDownLatch和CyclicBarrier的作用有点类似,就一并分析了。

CountDownLatch

  • 特点

是一个原子递减操作,当计数器为0的时候,所有等待的线程将释放,阻塞状态将打开。
只能使用一次,count值不能被外界修改。
主要是当当前计数器==0的时候,等待的线程将释放阻塞。可以一个异步执行,比如Zookeeper的连接就可以这么干。
修改count的值可以是一个线程也可以是多个线程

  • 类图
    在这里插入图片描述

实现逻辑

  1. 初始化CountDownLatch,会有count参数传入,该count参数表示计数器的初始值。
  2. await方法,当调用await方法时,会去检查是否需要等待,CountDownLatch的Sync类重写了tryAcquireShared方法,该方法在count=0的时候返回1,否则返回-1,所以count必须是一个非负数
    1. count>0的情况下,表示该线程需要等待,进入AQS的等待队列。
    2. count==0的情况下,那么该线程将会直切返回,不会自旋且入队。如果该线程已经是处于等待队列中的时候,那么,将会出队并退出自旋。
  3. countDown方法,该方法负责去对count去做–操作,当当前计数器为0的时候,将会直接返回,否则将自旋+CAS进行count–。

源码分析

CountDownLatch.Sync

        //这里是await将会调用该方法
        protected int tryAcquireShared(int acquires) {
            //在count==0的时候直接返回1,否则返回-1,返回-1就表示该线程需要进行自旋入队
            return (getState() == 0) ? 1 : -1;
        }
        
        //countDown方法将会调用该方法,该方法一定会有返回结果,是一个类同步方法
        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                //count==0表示不需要进行--操作,直接返回
                if (c == 0)
                    return false;
                //这里通过CAS来进行赋值,加上自旋操作,来保证该方法一定会产生数据变更
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    //当当前count的值==0的时候返回true将进行doReleaseShared操作,否则不做任何操作
                    return nextc == 0;
            }
        }

AbstractQueuedSynchronizer相关源码

//实际做Acquire的方法(当count!=0的时候)
 private void doAcquireShared(int arg) {
        //入队
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            //自旋操作
            for (;;) {
                //得到上面入队的node的前继
                final Node p = node.predecessor();
                //当p是队首的话,将会再次count的值
                if (p == head) {
                    //再次判断,这里count==0的时候,r=1
                    int r = tryAcquireShared(arg);
                    //当不需要等待的时候,就去释放head
                    if (r >= 0) {
                        //这个时候其实需要进行出队操作了,需要将node设置为head节点
                        setHeadAndPropagate(node, r);
                        //清理出队的前head
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //这里是Condition相关的操作
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    
    private void setHeadAndPropagate(Node node, int propagate) {
            Node h = head;
            //将此node设置成head节点
            setHead(node);
            //propagate 就是 tryAcquireShared的值,当count==0为1
            if (propagate > 0 || h == null || h.waitStatus < 0 ||
                (h = head) == null || h.waitStatus < 0) {
                Node s = node.next;
                //如果没有获取节点或者是共享节点
                if (s == null || s.isShared())
                    //释放
                    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;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                //共享模式(ws==0只有第一次进入该方法有效)
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

CyclicBarrier

实现逻辑

当所有await的线程数达到初始值时,阻塞的线程将会继续顺序执行。有点像先让线程初一某个屏障,任何线程都不能穿越该屏障,知道线程数达到期望值的时候,那么就会改期屏障,所有线程都会执行。

源码

private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        //排它锁,因为多个线程来修改count的时候会出现线程不安全的问题,count的共享资源
        final ReentrantLock lock = this.lock;
        //进入await方法必须先获取锁
        lock.lock();
        try {
            //来标识,是否被中断
            final Generation g = generation;
            //中断就抛异常
            if (g.broken)
                throw new BrokenBarrierException();
            //这里线程被中断也会抛异常
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            //等待线程+1,原始值-1
            int index = --count;
            //当前count==0标识这屏障就要是要,所有线程将会自由飞翔
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    //这里会从构造方法传入一个callback对象,在屏障点进行回调执行
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    //唤醒其他线程,并且重置generation
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        //如果command执行出现异常,那么将会中断此次屏障
                        breakBarrier();
                }
            }
            
            //index!=0的情况,以为着需要等待屏障解除
            // loop until tripped, broken, interrupted, or timed out
            for (;;) {
                try {
                    //是否设置了超时,没有设置,那么该线程等待,释放锁资源,进入等待queue
                    if (!timed)
                        trip.await();
                    //设置了超时的情况 只是加了一个超时判断
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    //此处是线程被中断
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }
                //中断
                if (g.broken)
                    throw new BrokenBarrierException();
                 //这里做了double check generation也是共享资源
                //其他线程重置了generation,这里直接退出自旋
                if (g != generation)
                    return index;
                //超时了
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            //最后将释放锁,其他在争抢队列中的线程将去获取锁
            //count==0的时候
            lock.unlock();
        }
    }
    
    private void nextGeneration() {
            // signal completion of last generation
            //这里唤醒所有等待的线程
            trip.signalAll();
            // set up next generation
            //count==0 parties=初始值
            count = parties;
            //重置generation字段
            generation = new Generation();
        }

比较

CountDownLatch更像是一个计数器,join操作。
CyclicBarrier像是一个多线程状态一致后的再次执行,现实生活中的比赛。

GitHub主页

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值