CountDownLatch源码剖析

  CountDownLatch 计数器设置默认的 count ,然后调用 await() 的线程会一直被挂起,直到 count 个线程都执行 countDown() 之后,被挂起的线程才会唤醒,接着走下面的逻辑。

    public static void main(String[] args) throws Exception{
        CountDownLatch latch = new CountDownLatch(2);

        new Thread(()->{
            try {
                Thread.sleep(2000);
                System.out.println("线程1准备执行 countDown 操作");
                latch.countDown();
            }catch (Exception e){
                e.printStackTrace();
            }
        }).start();

        new Thread(()->{
            try {
                Thread.sleep(2000);
                System.out.println("线程2准备执行 countDown 操作");
                latch.countDown();
            }catch (Exception e){
                e.printStackTrace();
            }
        }).start();

        System.out.println("main() 线程准备执行 await");
        latch.await();
        System.out.println("success");
    }

CountDownLatch

  我们在创建 CountDownLatch 的时候,其实他就是一个 sycn 、继承了 aqs,可以猜到它的实现应该也是aps 那一套。首先就是将 count 赋值给了 state 标记锁状态。

await

  主线程在 await() 的时候就等在了那里,直到所有线程执行完 countDown() , 主线程就继续往下执行。猜想他应该是进入了 aqs 的同步队列,直到其他线程将他唤醒。那我们就看看它的具体实现。

1. tryAcquireShared() 获取 state ,我们初始化的时候设置的是2,那么此时肯定 !=0 。
2. doAcquireSharedInterruptibly() 首先在 addWaiter() 里面for循环中循环两次将 当前线程加入等待队列中。
3. 然后获取到上一个节点 p, 它肯定是一个空的头节点。
4. 然后 tryAcquireShared() 再次判断一下锁占用状态。
5. setHeadAndPropagate() 将当前节点设置为头节点(因为可能不止一个地方吊桶 await() ,所以通过指针的变化一个个遍历出来)
6. 此时 r 肯定是>0 的,然后就 p.next=null 将自己从等待队中摘除出来
7. 最后在 parkAndCheckInterrupt() 里面调用 LockSupport.park() 将自己挂起,等待别人的唤醒。

countDown

1. tryReleaseShared() 先获取 state ,如果 !=0 说明有人持有锁,那就 cas 操作 -1 返回true。
2. doReleaseShared() 首先获取到头节点 h ,肯定不会为空的。
3. 然后如果状态是 Node.SIGNAL 那就 cas 改成 0 。
4. 最后在 unparkSuccessor() 里面获取头节点的下一个节点,也就是我们放在 同步队列 里面的main线程节点,通过 LockSupport.unpark() 唤醒它。

 

原文地址:https://www.cnblogs.com/wlwl/p/15046807.html