CountDownLatch 源码分析

CountDownLatch 源码分析:

1:CountDownLatch数据结构

成员变量 Sync类型对象

private final Sync sync; Sync是继承AQS的一个类,CountDownLatch是通过AQS和CAS来实现它的锁的功能的;

2构造方法:

public CountDownLatch(int count) {  //需要等待调用countDown() 的次数 这个例子中count=5 来举例说明

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

        this.sync = new Sync(count);  //创建Sync对象,AQS的子类

}

Sync(int count) {

            setState(count);   //设置AQS类中state=count=5 state 是volitle修饰的

        }

 

3:await 方法分析:

public void await() throws InterruptedException { 

        sync.acquireSharedInterruptibly(1);  //调用AQS中的acquireSharedInterruptibly方法

}

AQS. acquireSharedInterruptibly()  源码如下:

public final void acquireSharedInterruptibly(int arg)   // arg=1

            throws InterruptedException {

        if (Thread.interrupted())   // 当前线程是否中断状态 如果中断则抛出异常

            throw new InterruptedException();

        if (tryAcquireShared(arg) < 0)  //尝试获取共享锁

            doAcquireSharedInterruptibly(arg);

}

tryAcquireShared方法如下:

protected int tryAcquireShared(int acquires) {  // acquires=1

            return (getState() == 0) ? 1 : -1;  //state=5 表明未获取到锁,则返回 -1

        }

返回-1后满足tryAcquireShared(arg) < 0这个条件,则进入doAcquireSharedInterruptibly(arg);

这个方法:下面对这个方法分析:

在分析这个源码之前,说明下 for(;;) 的作用:不断的轮询,相当于while(true)

private void doAcquireSharedInterruptibly(int arg)    // arg=1

        throws InterruptedException {

创建当前线程的节点,并且锁的模型是 共享锁 将其添加到AQS CLH队列的末尾

        final Node node = addWaiter(Node.SHARED);

        boolean failed = true;

        try {

            for (;;) {   //轮询以下代码的逻辑

                final Node p = node.predecessor(); //获取前继节点

                if (p == head) {  //前继节点是表头则进入以下逻辑

                    int r = tryAcquireShared(arg); //尝试获取锁 这里state=5 获取锁失败

                    if (r >= 0) {

                        setHeadAndPropagate(node, r);

                        p.next = null; // help GC

                        failed = false;

                        return;

                    }

                }

//前继节点不是表头或者获取共享锁失败则进入以下逻辑,当前挂起,一直到获取到共享锁

                if (shouldParkAfterFailedAcquire(p, node) && 

                    parkAndCheckInterrupt())  //当前线程挂起,一直等待

                    throw new InterruptedException();

            }

        } finally {

            if (failed)

                cancelAcquire(node);

        }

}

 

下面对shouldParkAfterFailedAcquire 这个方法进行分析,这个之前在公平锁中有过分析

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

        int ws = pred.waitStatus;  //for(;;)第一次进入是waitStatus=0

        if (ws == Node.SIGNAL)

           return true;    //第二次进入的时候返回 true

        if (ws > 0) {

           

do {

                node.prev = pred = pred.prev;

            } while (pred.waitStatus > 0);

            pred.next = node;

        } else {

          

compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //设置waitStatus=-1

        }

        return false;

    }

接下来分析下 countDown方法:

public void countDown() {

        sync.releaseShared(1);

    }

releaseShared的方法如下:  目的  将AQS中的state -1,当state减到0的时候返回true

public final boolean releaseShared(int arg) {  // arg=1

        if (tryReleaseShared(arg)) {

            doReleaseShared();   //这段代码的作用是unpark 被await 的线程;

具体的实现是:调用后继节点的LockSupport.unpark(s.thread); 这里的s节点就是head节点的后继节点;也就是前面调用await被挂起的线程节点

            return true;

        }

        return false;

    }

通过这里的countDown方法,就和之前的await()方法呼应起来了。实现了,某一个线程等待其他线程执行结束后再执行这个线程

posted @ 2019-07-19 17:20  beppezhang  阅读(580)  评论(0编辑  收藏  举报