JUC锁框架源码阅读-CountDownLatch

说明

使用方式参考:https://www.cnblogs.com/LQBlog/p/8983019.html

初始化

main 

   public static void main(String[] args) throws InterruptedException {
        //<1>初始化
        CountDownLatch countDownLatch=new CountDownLatch(3);
    }

<1>初始化

  public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        //初始化内部类的对象 Sync 改类继承AQS
        this.sync = new CountDownLatch.Sync(count);
    }

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            //调用父类的设置所属状态 这里就表名锁以及被持有量额
            setState(count);
        }

    }

await等待

main

 public static void main(String[] args) throws InterruptedException {
        //初始化 默认设置AQS state为3 表示锁已经被持有
        CountDownLatch countDownLatch=new CountDownLatch(3);
        //<1>本质是加锁
        countDownLatch.await();
    }

1.因为初始化设置了栅栏数量 AQS state

2.实现获取共享锁的是判断 state是否>0 大于0则获取失败 交给AQS 放入CLH队列等待

 

<1>await

 public void await() throws InterruptedException {
        //<2>调用父类获取AQS方法
        sync.acquireSharedInterruptibly(1);
    }

<2>doAcquireSharedInterruptibly

详情可以参考《AQS源码阅读》

 /**
     * Acquires in shared interruptible mode.
     * @param arg the acquire argument
     */
    private void doAcquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //创建共享节点 并插入队列
        final AbstractQueuedSynchronizer.Node node = addWaiter(AbstractQueuedSynchronizer.Node.SHARED);
        boolean failed = true;
        try {
            //自旋
            for (;;) {
                //获得节点的上一个节点
                final AbstractQueuedSynchronizer.Node p = node.predecessor();
                //如果他的节点就是节点头 尝试获取锁 因为并发情况节点头可能释放了
                if (p == head) {
                    //<3>子类实现获取共享锁方法模板模式
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        //重新设节点头 并尝试唤醒其他等待节点
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //如果前一个节点p的状态是Node.SIGNAL,就是调用<6>parkAndCheckInterrupt方法阻塞当前线程
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())

                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                //<此时前一个节点pred的状态只能是0或者PROPAGATE,不可能是CONDITION状态
                // CONDITION(这个是特殊状态,只在condition列表中节点中存在,CLH队列中不存在这个状态的节点)
                // 将前一个节点pred的状态设置成Node.SIGNAL,这样在下一次循环时,就是直接阻塞当前线程
                cancelAcquire(node);
        }
    }

<3>tryAcquireShared

java.util.concurrent.CountDownLatch.Sync#tryAcquireShared

   protected int tryAcquireShared(int acquires) {
        //获取state 前面我们默认值设置的3 所以await是获取不到的 所以加入AQS队列
        return (getState() == 0) ? 1 : -1;
    }

countDown释放

main

  public static void main(String[] args) throws InterruptedException {
        //初始化 默认设置AQS state为3 表示锁已经被持有
        CountDownLatch countDownLatch=new CountDownLatch(3);
        //<1>本质是释放锁
        countDownLatch.countDown();
    }

1.每次释放 栅栏数量-1(AQS sate) 如果state

2.当state数量大于==0 则返回释放成功 交给AQS 唤醒AQS阻塞队列 这个时候因为state等于0 所以都获取成功锁

<1>countDown

 public void countDown() {
        //<2>调用内部内对象释放共享锁的方法 继承自AQS
        sync.releaseShared(1);
    }

<2>releaseShared

可以参考《AQS源码阅读》

 // 释放共享锁
    public final boolean releaseShared(int arg) {
        // <3>模板模式 抽象方法尝试释放共享锁
        if (tryReleaseShared(arg)) {
            //唤醒等待共享锁的线程
            doReleaseShared();
            return true;
        }
        return false;
    }

<3>tryReleaseShared

java.util.concurrent.CountDownLatch.Sync#tryReleaseShared

   protected boolean tryReleaseShared(int releases) {
        //自旋
        for (;;) {
            //获得状态
            int c = getState();
            //如果已经等于0 返回false
            if (c == 0)
                return false;
            int nextc = c-1;
            //cas设置状态-1 当成功并且等于0 表示释放成功 会唤醒阻塞线程
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }

 

posted @ 2021-09-03 17:56  意犹未尽  阅读(33)  评论(0编辑  收藏  举报