源码解析-CountDownLatch

CountDownLatch 作为JUC并发包下一重要工具

Latch的意思是栅栏,作为一个阀门用来控制多个线程的

常用方法: new CountDownLatch(参数为放开栅栏的条件);     countDown(); 条件-1           await();  条件未达到0则阻塞住,达到0则放开栅栏 

两个简单的应用场景

场景一   五个线程必须都执行完,才能继续往下,任一个线程没执行完都会await()处阻塞住

 

 

场景二   countDownLatch02.await()阻塞在每个线程内部 保证了每个线程都初始化完成,在同一起跑线进行下一步。countDownLatch01的作用同上

 

 

源码分析

 

 

 

老套路了,CountDownLatch下定义了一个内部类Sync 继承自AQS

so 还是AQS思想 

大体思想就是:new CountDownLatch(n)  AQS的state设置为n

每次CountDown() state-1    线程await()时判断state是否为0,如果不为0就加入到AQS的队列,然后将线程挂起。当state==0时 将线程从队列中一个一个唤醒

OK 到这里就可以结束了,下边是具体源码分析。

 

1.  new CountDownLautch(参数)  如上所述 没啥好说的

 

 

 

 

 

 

2.  await()方法  

 

acquiresharedInterruptibly(参数传1)  参数并没什么意义

 

如果当前state不为0,栅栏没有满足打开的条件,则将线程加入队列并挂起

 

3.  tryAcquireShared() 用来判断当前state是否为0  

 

 4. doAcquireSharedInterruptibly()     将线程封装成节点,加入到队列,然后挂起等待唤醒或中断

 

将线程封装成节点,加入到队列;           //可以参考https://www.cnblogs.com/ttaall/p/13828134.html 这篇中的addWaiter()部分

boolean failed = true;

try {

   for(死循环) {

      获取当前节点的前置节点p;

      if(如果前置节点是头节点) {
        判断当前是否state==0;

        如果栅栏可以打开{

          唤醒头节点并传播下去;

          failed = false;

          return;  //一切顺利并返回

        }

      }

      if (线程是否应该挂起 并挂起 ){

        shouldParkAfterFailedAcquire()  //线程是否应该挂起
        parkAndCheckInterrupt()    //线程挂起,如果被中断则返回true

        抛中断异常

      }

 

    }

}finally {

  if (如果中途被中断了){

    解散节点,不玩了

  }

}

5. setHeadAndPropagate() 

 

 Node h  用来保存头节点

将当前节点设置为头节点;

if (满足条件){

  Node s = 当前节点的下一个节点;

  if (s是Null  ||  s的下一个节点需要唤醒){

    持续唤醒()

  }

 

}

 

posted @ 2020-11-06 14:50  六小扛把子  阅读(147)  评论(0编辑  收藏  举报