并发编程:CountDownLatch

首先CountDownLatch是JUC(java.util-concurrent)下面的并发编程工具类,JDK1.5才出现的。

CountDownLatch

是一个倒计时工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。

生活中的场景有:开会场景,咱们要等全部人都到期后才开会,所以来一个人空位总数就减一,直到空位为0时,就开始开会。

直接上demo代码:

/**
 * @author lawt
 * @date 2018-09-10 9:18
 * CountDownLatch使用案例
 * 模拟开会场景,当所有人到场后就开始开会
 **/
public class CountDownLatchDemo {
/**
     * 参会人员必须要20个
     */
    private static final int THREAD_TOTAL_NUM = 20;
/**
     * 计数器
     */
    private static final CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(THREAD_TOTAL_NUM);

public static void main(String[] args) throws InterruptedException {

for (int i = 0; i < THREAD_TOTAL_NUM; i++) {
int index = i + 1;
new Thread(() -> {
                try {
                    System.out.println("第" + index + "个人到场");
                    //这个人到会花的时间
                    Thread.sleep(new Random().nextInt(3000));
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                //第index人员到场
                COUNT_DOWN_LATCH.countDown();

            }).start();
        }
//检查是否全部人员都到场
        COUNT_DOWN_LATCH.await();
        System.out.println("人员都已经到场,可以开会了");
    }
}

运行结果:



如果仅仅是使用,到此就可以了,如果想了解深一点可以继续往下看:

CountDownLatch的几个方法

  • await():如果当前count大于0,当前线程将会wait,直到count等于0或者中断。PS:当count等于0的时候,再去调用await(),线程将不会阻塞,而是立即运行。后面可以通过源码分析得到。

  • await(long timeout, TimeUnit unit):使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。

  • countDown(): 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。

  • getCount() :获得计数的数量


CountDownLatch的构造函数


然后调用Sync的构造方法,使用AQS的state表示计数count


再到AQS中的setState()方法


另外state的定义volatile修饰的,说明具有可见性,就是当一个线程修改CountDownLatch(num)中的num时,其他线程是可见的。

countDown方法 


如果当前count大于0,则减一,




await方法


AQS:acquireSharedInterruptibly(int arg) 


CountDownLatch$Sync:int tryAcquireShared(int acquires)


这里的state就是最开始new CountDownLatch(int count),count等于state

如果获取共享锁继续调用doAcquireSharedInterruptibly(arg)

640?wx_fmt=png

for (;;) {//本质是等待共享锁的释放,死循环。

int r = tryAcquireShared(arg);//就判断尝试获取锁

这里要注意一下r的值就2种情况-1和1:

  • r为-1,latch没有调用countDown(),state是没有变化的导致state一直大于0或者调用了countDown(),但是state不等于0,直接在for循环中等待

  • r为1,证明countDown(),已经减到0,当前线程还在队列中,state已经等于0了.接下来就是唤醒队列中的节点 

当前节点不是头结点,当前线程一直等待,直到获取到共享锁


如果这里多个线程wait之间没有调用countDown(),线程都在等待

如下图:



释放共享锁,通知后面的节点。AQS中的doReleaseShared方法,这一块主要是AQS中的代码,之前已经讲过了,所以这里不重提了。


猜你喜欢

转载自blog.51cto.com/10983206/2564036