当前位置: 首页 > news >正文

CountDownLatch 详解

CountDownLatch 用法详解

  • CountDownLatch 详解
    • 1 原理
    • 2 常见用法
    • 3 方法介绍
    • 4 示例及使用

CountDownLatch 详解

CountDownLatch(倒计时门闩)是Java并发包中的一个工具类,用于协调多个线程之间的同步。它允许一个或多个线程等待其他线程完成操作后再继续执行。

CountDownLatch 通过一个计数器来实现,该计数器在初始化时设定,然后递减。当计数器值为0时,等待的线程会被唤醒。

1 原理

CountDownLatch是通过一个计数器来实现等待操作的,它的基本原理如下:

计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已完成任务,然后在闭锁上等待的线程就可以恢复执行任务。

在这里插入图片描述

2 常见用法

用法一

某一线程在开始运行前等待n个线程执行完毕。

  1. 创建 CountDownLatch 对象时,需要传入一个初始计数值。这个值表示需要等待的线程数量。
  2. 当一个线程完成了它的任务后,可以调用 countDown() 方法来递减计数器的值。
  3. 其他线程可以调用 await() 方法来阻塞等待,直到计数器值为0。

用法二

实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。

  1. 初始化一个共享的CountDownLatch(1),将其计数器初始化为1。
  2. 多个线程在开始执行任务前首先 coundownlatch.await()。
  3. 当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。

结构:

CountDownLatch的UML类图如下:

在这里插入图片描述

CountDownLatch 的数据结构很简单,它是通过"共享锁"实现的。它包含了sync对象,sync是Sync类型。Sync是实例类,它继承于 AQS 。

源代码:

package java.util.concurrent;import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class CountDownLatch {private final Sync sync;public CountDownLatch(int count) {if (count < 0) {throw new IllegalArgumentException("count < 0");} else {this.sync = new Sync(count);}}public void await() throws InterruptedException {this.sync.acquireSharedInterruptibly(1);}public boolean await(long timeout, TimeUnit unit) throws InterruptedException {return this.sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));}public void countDown() {this.sync.releaseShared(1);}public long getCount() {return (long)this.sync.getCount();}public String toString() {return super.toString() + "[Count = " + this.sync.getCount() + "]";}private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;Sync(int count) {this.setState(count);}int getCount() {return this.getState();}protected int tryAcquireShared(int acquires) {return this.getState() == 0 ? 1 : -1;}protected boolean tryReleaseShared(int releases) {int c;int nextc;do {c = this.getState();if (c == 0) {return false;}nextc = c - 1;} while(!this.compareAndSetState(c, nextc));return nextc == 0;}}
}

从源码中可以看到:

内部类 Sync 继承了 AbstractQueuedSynchronizer,实现了计数器的同步控制逻辑。

其中,tryAcquireShared(int acquires) 方法用于判断是否可以获取同步状态,当计数器为0时返回1,表示可以获取同步状态;

tryReleaseShared(int releases)方法用于释放同步状态,将计数器减1,并使用compareAndSetState(c, nextc)方法保证操作的原子性。

3 方法介绍

CountDownLatch 类主要提供了以下方法:

  1. public CountDownLatch(int count)

    • 构造函数,创建一个 CountDownLatch 对象,并设置初始的计数器值为 count 。
  2. public void await() throws InterruptedException

    • 阻塞当前线程,直到计数器值变为0。如果计数器已经为0,立即返回。如果在等待过程中被中断,则抛出 InterruptedException 异常。
  3. public boolean await(long timeout, TimeUnit unit) throws InterruptedException

    • 阻塞当前线程,直到计数器值变为0或者超时。如果在指定时间内计数器变为0,则返回 true ,否则返回 false 。如果在等待过程中被中断,则抛出 InterruptedException 异常。
  4. public void countDown()

    • 递减计数器的值,表示一个任务已经完成。
  5. public long getCount()

    • 获取当前计数器的值。

4 示例及使用

下面是一个简单的示例,演示了如何使用CountDownLatch来实现线程之间的同步:

import java.util.concurrent.CountDownLatch;public class Main {public static void main(String[] args) throws InterruptedException {final int threadCount = 3;CountDownLatch latch = new CountDownLatch(threadCount);// 创建并启动线程for (int i = 0; i < threadCount; i++) {Thread thread = new Thread(() -> {// 模拟线程执行任务try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread " + Thread.currentThread().getId() + " finished its task.");// 任务完成后递减计数器latch.countDown();});thread.start();}// 主线程等待所有子线程完成任务latch.await();System.out.println("All threads have finished their tasks.");}
}

在上面的示例中,主线程通过latch.await()等待所有子线程完成任务,而每个子线程完成任务后调用latch.countDown()来递减计数器。当计数器值变为0时,主线程恢复执行。

在实时系统中的使用场景

  1. 实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数器为1的CountDownLatch,并让其他所有线程都在这个锁上等待,只需要调用一次countDown()方法就可以让其他所有等待的线程同时恢复执行。
  2. 开始执行前等待N个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统都已经启动和运行了。
  3. 死锁检测:一个非常方便的使用场景是你用N个线程去访问共享资源,在每个测试阶段线程数量不同,并尝试产生死锁。

相关文章:

  • Java基础知识总结(48)
  • 106. 跑步锻炼(结果填空)
  • 蓝桥杯 2022 省 B 洛谷 P8787 砍竹子
  • Terminal常见快捷命令(持续更新)
  • 利用Leaflet + React:构建WEBGIS
  • 2024第十九届中国(温州)机械装备展9月20-22日举行
  • IDEA 宝贝插件
  • 靡语IT:Bootstrap 简介
  • 【Vue】组件
  • 集合类多线程(JUC)
  • Redis 的主从复制、哨兵和cluster集群
  • 腾讯云短暂崩溃2小时
  • Leetcode 150. 逆波兰表达式求值和Leetcode 55. 跳跃游戏
  • 原型设计模式的学习
  • Linux生态系统:探索Linux的开源世界
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 【EOS】Cleos基础
  • 【个人向】《HTTP图解》阅后小结
  • Java超时控制的实现
  • Mithril.js 入门介绍
  • mysql_config not found
  • Python学习笔记 字符串拼接
  • webpack+react项目初体验——记录我的webpack环境配置
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 力扣(LeetCode)56
  • 前端设计模式
  • 嵌入式文件系统
  • 算法-插入排序
  • 再次简单明了总结flex布局,一看就懂...
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • ​configparser --- 配置文件解析器​
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​Java并发新构件之Exchanger
  • ​批处理文件中的errorlevel用法
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • (02)vite环境变量配置
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (39)STM32——FLASH闪存
  • (C语言)fgets与fputs函数详解
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (编译到47%失败)to be deleted
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (七)Java对象在Hibernate持久化层的状态
  • (转)ORM
  • ***详解账号泄露:全球约1亿用户已泄露
  • **CI中自动类加载的用法总结
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例
  • .Net的DataSet直接与SQL2005交互
  • .NET应用架构设计:原则、模式与实践 目录预览
  • .project文件
  • /bin/bash^M: bad interpreter: No such file or directory