CountDownLatch详解——深入探究CountDownLatch源码

news2024/11/24 0:32:18

这篇文章将会详细介绍CountDownLatch这个并发类,通过深入底层源代码讲解其具体实现。

/**
 * A synchronization aid that allows one or more threads to wait until
 * a set of operations being performed in other threads completes.
 */

上面是CountDownLatch这个API的前两行注释,一句话已经说明了这个类的作用。

一种同步辅助工具,允许一个或多个线程等待其他线程正在执行的一组操作完成。

CountDownLatch是一个计数器,通过的构造方法指定初始计数器的值,调用CountDownLatch的countDown()方法让计数器的值减少1,当计数器的值变成0时,调用它的await()方法阻塞的当前线程会被唤醒,继续执行剩余的代码。

这个类的结构很简单,就不画类图了,直接贴出代码

package java.util.concurrent;

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

public class CountDownLatch {
    /**
     * Synchronization control For CountDownLatch.
     * Uses AQS state to represent count.
     */
    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) {
            return (getState() == 0) ? 1 : -1;
        }

        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;
            }
        }
    }

    private final Sync sync;

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

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

    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

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

    public long getCount() {
        return sync.getCount();
    }

    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}

CountDownLatch是通过AQS的实现类Sync来实现这个计数器功能的,通过AQS的state属性保存计数器的值。

因为这里的计数器值state是共享的,所以重写了AQS的tryAcquireShared()和tryReleaseShared()方法。

为什么要重写这两个方法呢?

因为AQS里的几个重要的方法aquire()和release()会根据当前是独占模式还是共享模式去调用对应的tryAcquire()/tryAcquireShared()、tryRelease()/tryReleaseShared()方法。

接下来,关注CountDownLatch的两个重要的方法:

 countDown()

让计数器的值减1

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

 在这个方法里调用了releaseShared()方法,因为静态内部类Sync没有重写这个方法,所以调用的是超类AbstractQueuedSynchronizer的方法。

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();

            return true;
        }

        return false;
    }

这个方法里先调用tryReleaseShared()方法,因为Sync重写了这个方法,所以调用的是Sync的方法。

        protected boolean tryReleaseShared(int releases) {

            for (;;) {
                // 获取state值,如果是0,直接返回false
                int c = getState();

                if (c == 0) {
                    return false;
                }

                // 通过Unsafe工具类的CAS方法尝试修改state的值为state - 1
                int nextc = c - 1;

                if (compareAndSetState(c, nextc)) {
                    // 修改完成之后,根据修改之前的state值是否为1返回true或false
                    return nextc == 0;
                }
            }
        }

 await()

阻塞当前线程,直到state值变成0才唤醒。

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

在方法内部调用了AQS的acquireSharedInterruptibly()方法,Interruptibly后缀的方法都可以被中断。

    public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

在这个方法中,先调用Thread类的静态方法interrupted()判断当前线程是否被中断,如果当前线程中断状态为true,则清除线程的中断标记并返回true。否则返回false。如果当前线程是被中断的状态,则抛出中断异常返回。关于interrupted()方法的详细介绍,请参考文章

interrupt()、interrupted()和isInterruptd()的区别icon-default.png?t=N7T8https://blog.csdn.net/heyl163_/article/details/132138422如果当前线程是正常的状态,调用tryAcquireShared()方法,因为Sync重写了这个方法,所以调用的是Sync的tryAcquireShared()方法。这个方法就是判断当前state属性值是否为0,如果不是0就返回-1,否则返回1

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

继续回到上面的代码,当state不为0时,会执行doAcquireSharedInterruptibly()方法,注意,这里的一个死循环for会让当前线程一直阻塞在这里,直到tryAcquireShared()获取到的返回值大于0,也就是1时才退出循环,而根据上面的方法,此时state值为0。

    private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;

        try {
            for (;;) {
                final Node p = node.predecessor();

                if (p == head) {
                    int r = tryAcquireShared(arg);

                    if (r >= 0) {
                        setHeadAndPropagate(node, r);

                        p.next = null; // help GC
                        failed = false;

                        return;
                    }
                }

                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

总结:这里的等待是通过无条件的for循环让当前线程一直等待,直到state的值变为0才退出循环返回。所以,在这里可以替代线程的join()方法使用,这也是CountDownLatch的主要用途。

好了,文章就分享到这里了,看完不要忘了点赞+收藏哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/950141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

对SAE的测评报告

对SAE的测评报告&#xff1a; 很高兴在这里跟大家分享我对SAE产品的测评体验&#xff0c;以下我将通过实验以及本期话题的问题这两个方面带领大家走进SAE&#xff1a; 产品介绍&#xff1a; Serverless应用引擎SAE是一款极简易用、自适应弹性的容器化应用平台。它提供全托管…

C++ 动态多态(虚函数)

所谓动态多态即&#xff0c;在运行过程中&#xff0c;会随参数的变化而展现不同的功能&#xff0c;其关键原理便是虚函数&#xff0c;与之对应的静态多态便是函数重载。 虚函数&#xff0c;即当存在与虚函数一样的函数&#xff0c;即返回值&#xff0c;函数名&#xff0c;参数等…

【毕业设计】基于SSM的电子图书分享系统

前言 &#x1f525;本系统可以选作为毕业设计&#xff0c;运用了现在主流的SSM框架&#xff0c;采用Maven来帮助我们管理依赖&#xff0c;所选结构非常合适大学生所学的技术&#xff0c;本系统结构简单&#xff0c;容易理解&#xff01;本系统功能结构完整&#xff0c;非常高适…

回归预测 | MATLAB实现IBES-ELM改进的秃鹰搜索优化算法优化极限学习机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现IBES-ELM改进的秃鹰搜索优化算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现IBES-ELM改进的秃鹰搜索优化算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图…

基于STM32的甲醛浓度检测报警仿真设计(仿真+程序+讲解)

仿真图proteus 8.9 程序编译器&#xff1a;keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;C0083 甲醛浓度检测报警仿真 1.主要功能2.仿真3. 程序4. 资料清单&下载链接 1.主要功能 功能说明&#xff1a; 1、以STM32单片机和控制核心设计甲醛浓度检测报警设计&…

Kubernetes技术--使用kubeadm快速部署一个K8s集群

这里我们配置一个单master集群。(一个Master节点,多个Node节点) 1.硬件环境准备 一台或多台机器,操作系统 CentOS7.x-86_x64。这里我们使用安装了CentOS7的三台虚拟机 硬件配置:2GB或更多RAM,2个CPU或更多CPU,硬盘30GB或更多 2.主机名称和IP地址规划 3. 初始化准备工作…

15.MyCat数据库分片

MyCat 是一个开源的数据库中间件&#xff0c;主要用于将数据库操作请求路由和分发到后端的多个数据库节点。 1.Mycat环境搭建 在两个不同数据库中创建相同表 下载mycat https://github.com/MyCATApache/Mycat-Serverhttps://github.com/MyCATApache/Mycat-Server 将下…

【线程同步】AQS抽象排队同步器(AbstractQueuedSynchronizer)

AQS(AbstractQueuedSynchronizer)抽象排队同步器 AbstractQueuedSynchronizer AQS就是AbstractQueuedSynchronizer类 AQS其实就是JUC包下的一个基类&#xff0c;JUC下的很多内容都是基于AQS实现了部分功能&#xff0c;比如ReentrantLock&#xff0c;ThreadPoolExecutor&#…

十二、集合(1)

本章概要 泛型和类型安全的集合基本概念 如果一个程序只包含固定数量的对象且对象的生命周期都是已知的&#xff0c;那么这是一个非常简单的程序。 通常&#xff0c;程序总是根据运行时才知道的某些条件去创建新的对象。在此之前&#xff0c;无法知道所需对象的数量甚至确切类…

热烈祝贺蜀益表面处理成功入选航天系统采购供应商库

经过航天系统采购平台的严审&#xff0c;眉山市蜀益表面处理科技有限公司成功入选中国航天系统采购供应商库。航天系统采购平台是航天系统内企业采购专用平台&#xff0c;服务航天全球范围千亿采购需求&#xff0c;目前&#xff0c;已有华为、三一重工、格力电器、科大讯飞等企…

Simulink建模与仿真(3)-Simulink 简介

分享一个系列&#xff0c;关于Simulink建模与仿真&#xff0c;尽量整理成体系 1、Simulink特点 Simulink是一个用来对动态系统进行建模、仿真和分析的软件包。使用Simulink来建模、分析和仿真各种动态系统(包括连续系统、离散系统和混合系统)&#xff0c;将是一件非常轻松的事…

natApp内网穿透工作原理

如图所示&#xff0c;用户启动内网穿透工具会将token传入natapp服务器与我们自己的主机建立一个类似于websocket的长链接&#xff0c;当从外网访问我们主机的接口时&#xff0c;会进行一个本地接口地址的截取&#xff0c;然后进行拼接成我们主机应用的真实地址。然后将数据返回…

AntDesign 自定义图片上传前端压缩画质

为什么压缩图片&#xff1f; 应为现在公司没有使用云数据库 从而为了减少服务器的消耗需要将用户上传的图片压缩 前端压缩图片的技术选择&#xff01; 查阅资料发现当下两种压缩的方法&#xff01;&#xff01;第一种使用工具库实现 npm install image-conversion --save 第…

JAVA集合汇总

数组&#xff1a; 固定长度&#xff0c;从栈中分配空间&#xff0c;对于程序方便快速&#xff0c;但是自由度小。 ​优点&#xff1a;下标定位&#xff0c;随机访问性强&#xff0c;查找速度快&#xff1b; ​缺点&#xff1a;插入、删除效率低&#xff0c;内存利用率低&…

前端最能打的本地存储方案

产品的原话就是“要又大又全”。既然存储量大&#xff0c;也要覆盖全多种设备多种浏览器。 方案选择 既然要存储的数量大&#xff0c;得排除cookielocalStorage&#xff0c;虽然比cookie多&#xff0c;但是同样有上限&#xff08;5M&#xff09;左右&#xff0c;备选websql 使…

SPSS--s04典型相关分析

典型相关基本原理 典型相关分析是主成分分析和因子分析的进一步发展 ,是研究两组变量间的相互依赖关系 ,把两组变量之间的相互关系变为研究两个新的变量之间的相关,而且又不抛弃原来变量的信息 ,这两个新的变量分别由第一组变量和第二组变量的线性组合构成 ,并且两组变量的个数…

以太网POE供电浪涌静电防护推荐TVS二极管

POE是一种传输技术&#xff0c;可在以太网电缆上传输电力和数据。1000M千兆以太网POE供电端口广泛用于安防、视频监控以及智能电网等工业系统&#xff0c;以实现系统内的数据、视频传输、流量控制、以及通过总线实现供电。由于工业以太网工作环境非常严酷苛刻&#xff0c;对于以…

Java不重启服务 扩展新支付功能demo

应用场景 对于支付&#xff1a;经常有些需求&#xff0c;需要我们扩展新的支付方式&#xff0c;但又不希望重启服务对于IOT&#xff1a;经常需要新增一些解析数据的Handler 那我们如何能做到不重启服务&#xff0c;就能扩展新功能呢&#xff1f;本文给一个简单的demo示例&…

无涯教程-Android - AutoCompleteTextView函数

AutoCompleteTextView是一个类似于EditText的视图&#xff0c;只是它在用户键入时自动显示补充数据。 AutoCompleteTextView - 属性 以下是与AutoCompleteTextView控件相关的重要属性。您可以查看Android官方文档以获取属性的完整列表以及可以在运行时更改这些属性的相关方法。…

循环冗余校验码(CRC校验)

CRC在线计算 CRC即循环冗余校验码&#xff08;Cyclic Redundancy Check&#xff09;&#xff1a;是数据通信领域中最常用的一种查错校验码&#xff0c;其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查&#xff08;CRC&#xff09;是一种数据传输检错功能&#xf…