• CountDownLatch类


    1.简述

      CountDownLatch是Java1.5之后引入的Java并发工具类(闭锁的一个实现),放在java.util.concurrent包下。用给定的计数初始化CountDownLatch。由于调用了countDown方法,所以在当前计数到达零之前,await方法会一直受阻塞。之后,会释放所有等待的线程,await的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用CyclicBarrier。

      CountDownLatch能够使一个或多个线程等待其他线程完成各自的工作后再执行。

      闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态。

      其他的N个线程必须引用闭锁对象,因为他们需要通知CountDownLatch对象,他们已经完成了各自的任务,这种机制就是通过countDown()方法来完成的。

      CountDownLatch优点

    • 对使用者而言,你只需要传入一个int型变量控制任务数量即可,至于同步队列的出队入队维护,state变量值的维护对使用者都是透明的,使用方便。

      CountDownLatch缺点

    • CountDownLatch设置了state后就不能更改,也不能循环使用。

      CountDownLatch使用场景

    • 确保某个计算在其需要的所有资源都被初始化之后才继续执行。
    • 确保某个服务在其依赖的所有其他服务都已启动后才启动。
    • 等待知道某个操作的所有者都就绪在继续执行。

    2.CountDownLatch常用方法

    /**构造方法
     */
    //构建了一个 CountDownLatch与给定的计数初始化。
    CountDownLatch(int count)
    
    /**常用方法
     */
    //获取当前count的值。
    long getCount()
    //让当前线程在此CountDownLatch对象上等待,可以中断。与notify()、notifyAll()方法对应。(注意:wait()方法是从Object类继承来的)
    void wait()
    //让当前线程等待此CountDownLatch对象的count变为0,可以中断。
    void await()
    //让当前线程等待此CountDownLatch对象的count变为0,可以超时、可以中断。
    boolean await(long timeout, TimeUnit unit)
    //使此CountDownLatch对象的count值减1(无论执行多少次,count最小值为0)。
    void countDown()
    View Code

    3.CountDownLatch的源码分析

      CountDownLatch内部实现是依赖于AQS共享锁(共享模式)来实现的。下面,我们分析CountDownLatch中的源码。

      CountDownLatch的主要属性

    //Sync内部类,实现AQS的state
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        
        // 传入初始次数
        Sync(int count) {
            setState(count);
        }
        // 获取还剩的次数
        int getCount() {
            return getState();
        }
        //尝试获取共享锁
        protected int tryAcquireShared(int acquires) {
            //注意:这里state等于0的时候返回的是1,也就是说count减为0的时候获取总是成功
            //state不等于0的时候返回的是-1,也就是count不为0的时候总是要排队
            return (getState() == 0) ? 1 : -1;
        }
        //尝试释放锁
        protected boolean tryReleaseShared(int releases) {
            for (;;) {
                //state的值
                int c = getState();
                //等于0了,则无法再释放了
                if (c == 0)
                    return false;
                //将count的值减1
                int nextc = c-1;
                //原子更新state的值
                if (compareAndSetState(c, nextc))
                    //减为0的时候返回true,这时会唤醒后面排队的线程
                    return nextc == 0;
            }
        }
    }
    
    //AQS同步器的实现类
    private final Sync sync;
    View Code

      CountDownLatch的构造函数

    /**需要传入一个count变量,也就是需要等待的线程数。
     */
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        //初始化状态数
        this.sync = new Sync(count);
    }
    View Code

      CountDownLatch的await方法

    /**该方法被调用时将会使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
     */
    public void await() throws InterruptedException {
        // 调用AQS的acquireSharedInterruptibly方法
        sync.acquireSharedInterruptibly(1);
    }
    /**该方法被调用时将会使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
     */
    public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
        // 调用AQS的tryAcquireSharedNanos方法
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
    View Code

      CountDownLatch的countDown方法

    public void countDown() {
        // 调用AQS的释放共享锁方法
        sync.releaseShared(1);
    }
    View Code

    4.CountDownLatch使用示例

    public class Test {
        public static void main(String[] args) throws Exception {
            /*创建CountDownLatch实例,计数器的值初始化为5*/
            final CountDownLatch downLatch = new CountDownLatch(5);
            /*创建三个线程,每个线程等待2s,表示执行比较耗时的任务*/
            for(int i = 0;i < 5;i++){
                new Thread(new Runnable() {
                    public void run() {
                        try {
                            Thread.sleep(2000);
                            System.out.println(String.format("线程%s已完成", Thread.currentThread().getName()));
                            /*任务完成后调用CountDownLatch的countDown()方法,进行减1*/
                            downLatch.countDown();
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
            /*主线程调用await()方法,等到其他五个线程执行完后才继续执行*/
            downLatch.await();
            System.out.print("所有线程都已经执行完成,继续运行主线程逻辑");
        }
    }
    View Code

    5.总结

      经过分析CountDownLatch的源码可知,其底层结构仍然是AQS,对其线程所封装的结点是采用共享模式,而ReentrantLock是采用独占模式。

      CountDownLatch主要是通过计数器state来控制是否可以执行其他操作,如果不能就通过LockSupport.park方法挂起线程,直到其他线程执行完毕后唤醒它。

  • 相关阅读:
    Java程序员必会的工具库,代码量减少90%
    Git常用操作
    Eclipse开发环境配置
    Spring Cloud Alibaba Nacos 在Windows10下启动
    MySQL连接异常Communications link failure
    Spring Cloud Alibaba Nacos2.0踩坑
    Spring Cloud Alibaba Nacos配置中心与服务发现
    RocketMQ学习笔记
    Linux开发环境配置
    Clumper尝鲜
  • 原文地址:https://www.cnblogs.com/bl123/p/14166734.html
Copyright © 2020-2023  润新知