CountDownLatch详解

简介: 功能描述一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 常见用法 多个人等一个信号后继续执行操作。例如5个运动员,等一个发令员的枪响。 一个人等多个人的信号。

功能描述

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
常见用法
多个人等一个信号后继续执行操作。例如5个运动员,等一个发令员的枪响。
一个人等多个人的信号。旅游团等所有人签到完成才开始出发。
我们最常见见到使用的地方是zk获取连接的时候

       final CountDownLatch countDownLatch=new CountDownLatch(1); 
       ZooKeeper zooKeeper=new ZooKeeper(zk_url, time_out, new Watcher() { 
           public void process(WatchedEvent watchedEvent) { 
               Event.KeeperState state = watchedEvent.getState(); 
               Event.EventType type = watchedEvent.getType(); 
               if(Event.KeeperState.SyncConnected==state){ 
                   if(Event.EventType.None==type){ 
                       //调用此方法测计数减一 
                       countDownLatch.countDown(); 
                   } 
               } 
           } 
       }); 
       //阻碍当前线程进行,除非计数归零 
       countDownLatch.await();

这里了也可以看到很明确的使用方法,countDown直到0,await的线程才会继续执行。

原理

CountDownLatch内部使用了共享锁。如果这里还不知道共享和独占的区别,可以看前面的aqs速读。
获取锁成功的方法很简单

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

共享锁有个约定,返回有三种情况。

  • 0为获取锁且没有其他资源
  • 正数 获取锁并且还有其他资源
  • 负数 获取锁资源失败

共享锁在tryAcquireShared返回大于0的值的时候,会唤醒其他停顿状态加锁线程。由于没有对state的增加操作,所以当state变成0的时候,所有尝试加锁的线程都会被唤醒。

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
        

释放锁的操作,就是把state的值减一,当只有state变成0的时候,才返回true,tryReleaseShared返回true的时候会触发唤醒其他加锁线程的操作。
通过上面的过程,我们可以看到CountDownLatch中的共享锁的加锁和释放锁的过程,下面看看是如何和CountDownLatch结合的。

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

CountDownLatch的构造里会初始化共享锁,并且设置state的值。

    public void countDown() {
        sync.releaseShared(1);
    }

countDown是释放锁,最终会调用到tryReleaseShared。

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

await是加锁,最终会调用到 tryAcquireShared。
CountDownLatch就是一个不断释放锁的过程。

常见问题

  1. CountDownLatch是不能够重用的
    根据上面的解析,大家也发现,共享锁加锁的操作并不会增加state的值。CountDownLatch中state一旦变成0就没有提供其他方式增长回去了。

所以CountDownLatch是一次性消耗品,用完就得换新的。这样设计也是比较正常的,对状态的要求比较严格,例如都开车了你再告诉司机,这次加了5个人,车都开了一半了,不会考虑再回去。随意修改约束值会带来很多逻辑上的问题。

  1. CountDownLatch无限等待
    countDown没有被调用,那么await就会一直等下去。countDown常见没有被调用的情况:异常中断,线程池拒绝策略。

可以使用

public boolean await(long timeout, TimeUnit unit)

根据业务情况,增加一个最大等待时间。使用这种方式,需要对失败的各种情况作出业务上的对应处理,否则就出现各种数据不正确的问题。也可以对countDown的线程做好异常处理,最好使用另外一个线程池来处理这些线程。这种情况就需要对业务不能停顿时间特别长,导致线程池的资源被耗光的情况做处理。如果是想通过new Thread避免,就需要考虑线程突然暴涨的问题。

目录
相关文章
|
7月前
|
设计模式 Java
CountDownLatch和CyclicBarrier源码详解
我现在有个场景:现在我有50个任务,这50个任务在完成之后,才能执行下一个函数,要是你,你怎么设计?可以用JDK给我们提供的线程工具类,CountDownLatch和CyclicBarrier都可以完成这个需求。基于AQS实现,会将构造CountDownLatch的入参传递至statecountDown()就是在利用CAS将state减1,await)实际就是让头节点一直在等待state为0时,释放所有等待的线程。
73 1
|
7月前
|
Java 测试技术
CountDownLatch、CyclicBarrier让线程听我号令
CountDownLatch、CyclicBarrier让线程听我号令
82 0
|
Java BI
CountDownLatch,CyclicBarrier,Semaphore
在开发过程中我们常常遇到需要对多个任务进行汇总,比如报表,或者大屏显示,需要将所有接口的数据都 获取到后再进行汇总,如果使用同步的方式,那么会比较耗时,体验不好,所以我们使用多线程,但是使用多线程 只能异步的执行,有些接口响应比较快,有些比较慢,而返回结果之间又有依赖,这样就无法汇总了, 所以我们引入了CountDownLatch,它能让所有子线程全部执行完毕后主线程才会往下执行,如果子线程没有执行完毕 ,那么主线程将无法继续向下执行。
67 0
|
7月前
CountDownLatch和CyclicBarrier你使用过吗?
CountDownLatch和CyclicBarrier你使用过吗?
45 0
|
7月前
|
Java
CountDownLatch的使用
CountDownLatch的使用
64 1
CountDownLatch 使用详解
本文主要对CountDownLatch 的相关知识点进行了介绍和讲解
133 1
CountDownLatch&CyclicBarrier&Semaphore
本文将介绍一下CountDownLatch 、 CyclicBarrier 、 Semaphore这几个控制线程的类。
 CountDownLatch&CyclicBarrier&Semaphore
CountDownLatch
CountDownLatch是从JDK1.5开始提供的一个辅助并发编程的一个类,它位于在JUC包中。 允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。
147 0
|
Java
CountDownLatch:别浪,等人齐再团!(2)
CountDownLatch:别浪,等人齐再团!(2)
108 0
CountDownLatch:别浪,等人齐再团!(2)