CountDownLatch源码分析

1、CountDownLatch介绍

CountDownLatch让一个或多个线程等待其他线程执行完成后再执行。在创建CountDownLatch对象时,必须指定线程数count,每当一个线程执行完成调用countDown()方法,线程数count减1,当count减到0时,await()方法就不再阻塞。

2、CountDownLatch源码分析

CountDownLatch详情如下:

2.1 构造函数

CountDownLatch没有无参构造函数,在有参构造函数中初始化了sync属性。

public CountDownLatch(int count) {

    // count 合法校验

    if (count < 0) throw new IllegalArgumentException("count < 0");

    // 初始化sync属性

    this.sync = new Sync(count);

}

2.2 Sync - 队列同步器

// 抽象队列同步器

private static final class Sync extends AbstractQueuedSynchronizer {

    private static final long serialVersionUID = 4982264981922014374L;



    // 将 count 赋值给 AQS 的 state 属性

    Sync(int count) {

        setState(count);

    }

    // 获取 AQS 的 state 属性

    int getCount() {

        return getState();

    }

    // 判断所有线程是否都执行完成, 1 -> 全部执行完成;-1 -> 仍有线程在执行

    protected int tryAcquireShared(int acquires) {

        return (getState() == 0) ? 1 : -1;

    }

    // 释放锁

    protected boolean tryReleaseShared(int releases) {

        // 自旋

        for (;;) {

            // 获取 AQS 的 state

            int c = getState();

            // 锁资源已经释放完毕,再次进入,直接返回false,什么也不做

            if (c == 0)

                return false;

            //  state - 1

            int nextc = c-1;

            // CAS 赋值操作

            if (compareAndSetState(c, nextc))

                // 最后一个线程执行完,state = 0 ,返回true。

                // countDown() 唤醒等待队列中的其他挂起线程

                return nextc == 0;

        }

    }

}

2.3 await() - 阻塞等待

CountDownLatch#await(),详情如下: 

// AQS的state属性不为0, 阻塞

 public void await() throws InterruptedException {

     // 调用AQS提供的获取共享锁并允许中断的方法

     sync.acquireSharedInterruptibly(1);

 }

AbstractQueuedSynchronizer#acquireSharedInterruptibly(),详情如下:

// 获取共享锁,并允许其中断

public final void acquireSharedInterruptibly(int arg) throws InterruptedException {

    // 线程中断,抛出异常

    if (Thread.interrupted())

        throw new InterruptedException();

    // 获取共享锁,由CountDownLatch实现

    if (tryAcquireShared(arg) < 0)

        // state > 0,说明有线程在持有锁资源,将当前线程添加到AQS等待队列中

        doAcquireSharedInterruptibly(arg);

}

CountDownLatch#Sync#tryAcquireShared(),详情如下:

// 获取共享锁

 protected int tryAcquireShared(int acquires) {

     // 线程全部执行完成,返回 1;未全部执行完成,返回-1

     return (getState() == 0) ? 1 : -1;

 }

AbstractQueuedSynchronizer#acquireSharedInterruptibly(),详情如下:

// 将当前线程添加到AQS等待队列中

private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {

    // 当前线程封装成Node,添加到AQS等待队列中

    final Node node = addWaiter(Node.SHARED);

    boolean failed = true;

    try {

        // 自旋

        for (;;) {

            // 获取当前线程节点的前驱节点

            final Node p = node.predecessor();

            // 前驱节点为等待队列头节点

            if (p == head) {

                // 调用 CountDownLatch 实现的方法

                int r = tryAcquireShared(arg);

                // 返回值为1,表示 state 为 0 ,所有线程都释放了锁,无其他线程持有锁资源

                if (r >= 0) {

                    // state = 0,将当前线程和后面所有排队的线程都唤醒。

                    setHeadAndPropagate(node, r);

                    p.next = null;

                    failed = false;

                    return;

                }

            }

            // *** 线程在此处被挂起,待所有线程释放锁资源后,即state = 0 ,线程被唤醒,再继续往下执行

            // 挂起获取锁资源失败的线程,并且挂起的线程被中断,抛出InterruptedException异常

            if (shouldParkAfterFailedAcquire(p, node) &&

                parkAndCheckInterrupt())

                throw new InterruptedException();

        }

    } finally {

        if (failed)

            cancelAcquire(node);

    }

}

2.4 countDown() - 释放锁资源

CountDownLatch#countDown(),详情如下:

// countDown方法, 实际上调用了AQS的释放共享锁操作

public void countDown() {

    sync.releaseShared(1);

}

AbstractQueuedSynchronizer#releaseShared(),详情如下:

// AQS提供的释放共享锁方法,CountDownLatch实现了 tryReleaseShared 方法

public final boolean releaseShared(int arg) {

    // 尝试释放锁资源

    if (tryReleaseShared(arg)) {

        // 没有线程持有锁资源,唤醒等待队列中的其他挂起线程

        doReleaseShared();

        return true;

    }

    return false;

}

CountDownLatch#Sync#tryReleaseShared(),详情如下:

protected boolean tryReleaseShared(int releases) {

    // 自旋

    for (;;) {

        // 获取当前持有锁资源的线程数

        int c = getState();

        // state已为0,返回false,那么再次执行countDown,什么事情也不做

        if (c == 0)

            return false;

        // count - 1

        int nextc = c-1;

        // CAS 完成赋值操作

        if (compareAndSetState(c, nextc))

            // 没有线程持有锁资源,返回true

            return nextc == 0;

    }

}

AbstractQueuedSynchronizer#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);

            }

            // CAS失败

            else if (ws == 0 &&

                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))

                continue;

        }

        // 等待队列头节点被改变,结束循环

        if (h == head)

            break;

    }

}

2.5 总结

CountDownLatch基于 AQS + CAS 实现,CountDownLatch的构造函数中必须指定count,同时初始继承AQS的内部类Sync,通过Sync对象将count赋值给AQS的state属性,这样就可以基于AQS提供的方法完成CountDownLatch的功能。

调用countDown()方法,实际上是将AQS中 state 减 1。所有线程执行完成,state 会被修改为 0 ,在countDown()中会唤醒等待队列中挂起的线程。

调用await()方法,实际上是判断AQS中的 state 是否为 0。state > 0,表示有线程仍在执行,此时await()会阻塞线程。当最后一个线程执行结束,state 变为 0,countDown()唤醒线程后,await()正常执行结束,不再阻塞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值