CountDownLatch源码分析

CountDownLatch源码分析


简介

CountDownLatch,我们称之为闭锁或倒计时器,它允许一个或多个线程必须等待其他线程全部执行完之后才能执行。例如:部门周会,必须等所有人到齐才能开。其本身是基于AQS的共享模式实现,示例图如下:
image_1dhq54g0elak1viunibj1klln9.png-28.5kB

方法摘要

methoddescription
public void countDown()在许可可用之前阻塞当前线程。
public void await() throws InterruptedException阻塞式地等待,并且是响应中断的
public boolean await(long timeout, TimeUnit unit) throws InterruptedException带超时等待,可响应中断

源码分析

private final Sync sync;

syncCountDownLatch的核心属性,它继承自AQS,同时重写了tryAcquireSharedtryReleaseShared,以完成具体的同步状态的获取与释放的逻辑。

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        /**
         * 构造方法里面用`count`值初始化`AQS`里的`state`,来表示计数器的总数
         */
        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }
        
        /**
         * state为0表示尝试获取同步状态成功,其他情况均为失败
         * 没有使用入参
         */
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
        /**
         * 每调用一次,state减1,直到state值为0表示释放同步状态成功
         * 自旋是为了防止CAS失败,没有使用入参
         */
        protected boolean tryReleaseShared(int releases) {
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

countDown方法

    public void countDown() {
        //每次
        sync.releaseShared(1);
    }

await方法

    public void await() throws InterruptedException {
        //调用可中断的获取同步状态方法
        sync.acquireSharedInterruptibly(1);
    }
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        //调用可超时的获取同步状态的方法
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

示例

模拟一下刚开始那张图的场景:

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(3);
        //新建三个线程
        for (int i = 1; i < 4; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                latch.countDown();
            },"r" + i).start();
        }


        for (int i = 1; i < 4; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "正在await" );
                    latch.await();
                    System.out.println(Thread.currentThread().getName() + "从await中返回" );
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"t" + i).start();
        }

    }

结果

image_1dhqtn5dj1f3ae6nqhotj19h3p.png-13.6kB

分析

刚开始,state的初始值为3,此时t1t2t3调用await(假设按照这个顺序,实际情况可能入队顺序不一样),await调用acquireSharedInterruptibly,接着调用tryAcquireShared,因为此时state值肯定不为0(r1,r2,r3三个线程执行3s),所以tryAcquireShared返回-1,于是执行doAcquireSharedInterruptibly,然后调用 parkAndCheckInterrupt被挂起,此时t1t2t3均加入同步队列,且对应的前驱waitStatus值置为-1,等待被唤醒。
image_1dhqvfim91tl31v3v1gmq16p2s541j.png-62.9kB

然后假设此时r1调用countDown,然后调用releaseShared,接着调用tryReleaseSharedstate值-1变为2,不等于0,返回false,直到r3线程调用countDown完毕之后,state变为0,返回true,此时调用doReleaseShared,然后调用unparkSuccessor唤醒t1,最后相继唤醒t2t3

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值