CountDownLatch 使用例子和代码流程

news2025/5/18 0:18:29

目录

    • CountDownLatch意思理解
    • 普通多线程运行
    • Thread.join()实现
    • CountDownLatch实现
    • CountDownLatch流程
      • new CountDownLatch(3)
      • countDown 方法
      • await方法

CountDownLatch意思理解

单词1: countdown


常见释义:[ˈkaʊntdaʊn][ˈkaʊntdaʊn]
n.	倒数读秒,倒计时(如发射宇宙飞船时); 大事临近的时期;
[例句]The countdown to Christmas starts here.
现在开始圣诞节倒计时。
[其他]	复数:countdowns

单词2: latch

常见释义 英[lætʃ][lætʃ]
n.	插销; 门闩; 弹簧锁; 碰锁;
vt.	用插销插上; 用碰锁锁上;
[例句]He lifted the latch and opened the door.
他拉起门闩开了门。
[其他]	第三人称单数:latches 复数:latches 现在分词:latching 过去式:latched 过去分词:latched

场景:完成某件事情时,前面的一些事情都要完成,然后自己才能继续。

远离:使用一个计数器进行实现,计数器初始值就是其它线程的数量。当每个被计数的线程完成任务后,计数器值减一,当计数器的值为0时,表示所有线程都已经完成了任务,然后在CountDownLatch上等待的线程就可以恢复执行。

普通多线程运行

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

        Runnable runnable = new Runnable() {
            @Override
            public void run () {
                try {
                    // 业务处理
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName() + ":" + " finished");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {

                }
            }
        };
        List<Thread> threads = new ArrayList<>(4);
        for(int i= 0;i<4;i++) {
            Thread thread = new Thread(runnable);
            threads.add(thread);
        }
        for(Thread thread: threads){
            thread.start();
        }

        System.out.println("main end");

    }

Thread.join()实现

如下,main线程和其它线程运行时序不定,达不到其它线程都结束后main县城再结束的效果。

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

        Runnable runnable = new Runnable() {
            @Override
            public void run () {
                try {
                    // 业务处理
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName() + ":" + " finished");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {

                }
            }
        };
        List<Thread> threads = new ArrayList<>(4);
        for(int i= 0;i<4;i++) {
            Thread thread = new Thread(runnable);
            threads.add(thread);
        }
        for(Thread thread: threads){
            thread.start();
        }
        for(Thread thread: threads){
            thread.join();
        }

        System.out.println("main end");

    }

join原理:

  • A线程中执行 B.join(),则A线程阻塞住,直到线程B完成,A线程才能再继续。

  • join方法的本质调用的是Object中的wait方法实现线程的阻塞,即有monitor锁的概念。所以B线程执行一半要通知A线程继续则无法实现。

CountDownLatch实现

public class Test {

    final static int CNT = 3;

    static CountDownLatch countDownLatch = new CountDownLatch(CNT);


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

        Runnable runnable = new Runnable() {
            @Override
            public void run () {
                try {
                    // 业务处理
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + ":" + "business finished");
                    countDownLatch.countDown();
                    // 其它处理
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName() + ":" + " finished");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                }
            }
        };
        List<Thread> threads = new ArrayList<>(4);
        for (int i = 0; i < CNT; i++) {
            Thread thread = new Thread(runnable);
            threads.add(thread);
        }
        for (Thread thread : threads) {
            thread.start();
        }
        countDownLatch.await();
        System.out.println("main end");
    }

}

输出如下: main线程只等待业务线程的业务逻辑结束就行。(当然应该写两个try finally, 避免结束不了)
在这里插入图片描述

改进如下:

public class Test {

    final static int CNT = 3;

    static CountDownLatch countDownLatch = new CountDownLatch(CNT);


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

        Runnable runnable = new Runnable() {
            @Override
            public void run () {
                // 业务处理
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + ":" + "business finished");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
                // 其它处理
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName() + ":" + " finished");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                }

            }
        };
        List<Thread> threads = new ArrayList<>(4);
        for (int i = 0; i < CNT; i++) {
            Thread thread = new Thread(runnable);
            threads.add(thread);
        }
        for (Thread thread : threads) {
            thread.start();
        }
        countDownLatch.await();
        System.out.println("main end");
    }

}

CountDownLatch流程

new CountDownLatch(3)

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

CountDownLatch内部自己实现了继承AbstractQueuedSynchronizer的同步器,如下

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

countDown 方法

在这里插入图片描述

await方法

在这里插入图片描述

CountDownLatch 并没有往aqs队列加入节点,而是使用aqs的共享模式

aqs的java.util.concurrent.locks.AbstractQueuedSynchronizer#doAcquireSharedInterruptibly方法

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);
    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
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        throw t;
    }
}

只要countDown没有减到0,那么Node.SHARED就是head, 则一直for(;😉 死循环的执行判断countDown为0,只要为0,就返回退出了

  if (p == head) {
   int r = tryAcquireShared(arg);
      if (r >= 0) {
          setHeadAndPropagate(node, r);
          p.next = null; // help GC
          return;
      }
  }

其中tryAcquireShared(arg);则是CountDownLatch中d的判断逻辑 java.util.concurrent.CountDownLatch.Sync#tryAcquireShared

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

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

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

相关文章

王江涛十天搞定考研词汇

学习目标&#xff1a; 考研词汇 学习内容&#xff1a; 2023-9-17 第一天考研词汇 学习时间&#xff1a; 2023-9-17 学习产出&#xff1a;A intellect智力&#xff1b;知识分子intellectual智力的&#xff1b;聪明的intellectualize使...理智化&#xff0c;对...做理性探索c…

ros2学习笔记:shell环境变量脚本setup.bash[-z][-n][-f]参数作用

-n作用 [ -n 字符串 ] or [ 字符串 ] 字符串的长度为非零&#xff08;有内容&#xff09;则为真。加-n与不加-n结果相同。 -z作用 [ -z 字符串 ] 字符串的长度为零则为真。 字符串为空即NULL时为真&#xff0c;与上面的-n相反。 -f作用 [ -f FILE ] 如果 FILE 存在且是一…

Unity shader内置standard代码解析

最近有相关需求制作&#xff0c;所以这里编写一个文档&#xff0c;方便后续的流程查看。 下载源码 由于unity内置的shader是无法查看源码的&#xff0c;你需要去官网下载对应版本内置源码查看 在引擎下载那里&#xff0c;会有一个Built in Shaders&#xff0c;下载 打开以后…

刷一下算法

记录下自己的思路与能理解的解法,可能并不是最优解法,不定期持续更新~ 1.盛最多水的容器 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容…

AURIX TC3XX内存映射分析

TC3XX内存映射Features AURIX TC3xx系列设备的内存映射中包含的各个部分。这些内存部分在设备上有各自的用途和特性。以下是这些部分的一些概念解释&#xff1a; Program Flash Interface (PFI) 和 Program Flash Memory (PF) 是用来存储程序代码的闪存。即使在断电时&#xf…

【学习笔记】Java 一对一培训(3.1)Spring Boot介绍和基础

【学习笔记】Java 一对一培训&#xff08;3.1&#xff09;Spring Boot介绍和基础 关键词&#xff1a;Java、Spring Boot、Idea、数据库、一对一、培训、教学本文主要内容含Spring Boot相关的基础介绍、快速入门、Maven介绍、代码结构介绍、打包运行、配置介绍等计划1小时完成&…

TOTP算法实现

TOTP算法实现 1 什么是双因子认证&#xff08;2FA&#xff09;2 TOTP原理2.1 HOTP原理2.2 TOTP 3 实现参考文章 最近发现github天天给我发通知要启用双因子认证&#xff08;2FA&#xff09;&#xff0c;受不了了只能想办法启用了。看到它支持采用基于TOTP算法的应用的认证方式&…

彻底搞懂线程池原理以及创建方式

1. 为什么要使用线程池 在实际使用中&#xff0c;线程是很占用系统资源的&#xff0c;如果对线程管理不善很容易导致系统问题。因此&#xff0c;在大多数并发框架中都会使用线程池来管理线程&#xff0c;使用线程池管理线程主要有如下好处&#xff1a; 降低资源消耗。通过复用…

Python机器学习实战-建立Gradient Boosting模型预测肾脏疾病(附源码和实现效果)

实现功能 建立Gradient Boosting模型预测肾脏疾病 实现代码 import pandas as pd import warnings warnings.filterwarnings("ignore") pd.set_option(display.max_columns, 26)#读取数据 df pd.read_csv("E:\数据杂坛\datasets\kidney_disease.csv") …

vMAP——论文解析

vMAP: Vectorised Object Mapping for Neural Field SLAM vMAP 是一个物体级稠密图 neural SLAM&#xff0c;每一个物体都用一个 mlp 来表征&#xff0c;而不需要 3D 先验。当 RGB-D 相机在没有任何先验信息的情况下时&#xff0c;vMAP 会即时检测物体 instance&#xff0c;并将…

在Ubuntu 18.04上支持C++17的std::filesystem的方法

在Ubuntu 18.04上通过命令sudo apt install gcc g安装的gcc/g版本为7.5&#xff0c;此版本并不直接支持filesystem&#xff0c;如下图所示&#xff1a; Ubuntu 18.04上的g 7.5支持experimental的filesystem,即std::experimental::filesystem&#xff0c;若想使Ubuntu 18.04支持…

购物系统设计与实现

目 录 1 绪 论 1 1.1 本课题研究的背景和意义 1 1.1.1 本课题研究的背景 1 1.1.2 本课题研究的意义 2 1.1.3 本课题的发展现状及前景 2 1.2 系统的实现任务 7 2 系统概述及实现技术介绍 8 2.1 网上商城简介 8 2.2 相关实现技术介绍 10 2.2.1 JSP语言及其特点 10 2.2.2 Dreamwe…

快速学会搭建微信小程序的基础架构

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 基础架构 构建界面 引入 uni-ui 组件库 组件自动引入 配置TS类型 状态管理 持久化 数据交互 请…

Unity中Shader特性PerRendererData

文章目录 前言一、优化前是对使用了相同材质球的不同物体间shader分别设置&#xff0c;比较消耗性能二、使用[PerRendererData]标签&#xff0c;可以在脚本中使用SetPropertyBlock()对使用同一材质球的不同物体进行修改其Shader属性 前言 Unity中Shader特性PerRendererData 一…

Python学习 -- 常用数据交换格式(CSV、XML、JSON)

数据交换格式是在不同系统之间交换数据时使用的一种标准化格式。在Python中&#xff0c;我们常用的数据交换格式有CSV、XML和JSON。本篇技术博客将介绍这三种数据交换格式的详细使用方法&#xff0c;并提供具体的代码案例&#xff0c;帮助初学者快速掌握这些格式的使用。 CSV&…

第二章 进程与线程 六、线程的实现方式和多线程模型

目录 一、线程的实现方式 1、用户级线程 2、内核级线程 二、多线程模型 注意&#xff1a; 1、一对一模型 &#xff08;1&#xff09;定义: &#xff08;2&#xff09;优点&#xff1a; &#xff08;3&#xff09;缺点&#xff1a; 2、多对一模型 &#xff08;1&…

Linkerd的部署与入门--service mesh初步体验

Linkerd2初探 部署环境Linkerd简介安装Linkerd客户端在k8s上安装Linkerd控制平面&#xff08;服务端&#xff09;实验&#xff1a;数据平面代理注入demo应用安装viz插件&#xff08;可视化面板&#xff09;部署grafana 其他 部署环境 k8s环境: KIND 模拟kubernetes 1.21.1 kub…

【python】使用Reddit API爬取数据

这篇文章介绍如何使用reddit api获数据,文档地址如下:https://www.reddit.com/dev/api/ 首先需要创建应用,页面如下:https://www.reddit.com/prefs/apps 这里name随意填写,reditect uri随意写一个网址 如图所示,创建好应用以后,可以得到CLIENT_ID和SECRET_KEY: 编写代…

线性回归网络

李沐大神的《动手学深度学习》&#xff0c;是我入门机器学习的首课&#xff0c;因此在这里记录一下学习的过程。 线性回归的从零开始实现 线性回归是理解机器学习的基础&#xff0c;它经常用来表示输入和输出之间的关系。   线性回归基于几个简单的假设&#xff1a; 首先&am…

【计算机视觉】Vision and Language Pre-Trained Models算法介绍合集(一)

文章目录 一、ALIGN二、Contrastive Language-Image Pre-training&#xff08;CLIP&#xff09;三、Learning Cross-Modality Encoder Representations from Transformers&#xff08;LXMERT&#xff09;四、BLIP: Bootstrapping Language-Image Pre-training五、Vision-and-La…