JUC框架(Semaphore、CountDownLatch、CyclicBarrier)

news2024/9/24 21:23:28

在这里插入图片描述

文章目录

    • Semaphore(信号量)
      • Semaphore介绍
      • Semaphore基本概念
      • Semaphore使用场景
      • Semaphore示例
    • CountDownLatch (计数器/闭锁)
      • CountDownLatch 介绍
      • CountDownLatch 基本概念
      • CountDownLatch 使用场景
      • CountDownLatch 基本方法
      • CountDownLatch 示例
    • CyclicBarrier(循环栅栏)
      • CyclicBarrier介绍
      • CyclicBarrier基本概念
      • CyclicBarrier使用场景
      • CyclicBarrier基本方法
      • CyclicBarrier示例(银行流水)
    • CyclicBarrier 和 CountDownLatch 的区别

更多相关内容可查看

Semaphore(信号量)

Semaphore介绍

Semaphore(信号量)是Java并发包java.util.concurrent中的一个类,它主要用于控制对多个共享资源的访问。与CountDownLatch和CyclicBarrier等并发工具不同,Semaphore通常用于限制对某个资源池(或称为资源集)的并发访问数量

Semaphore基本概念

  • 许可(Permits):Semaphore管理一组虚拟的许可,每个许可代表对一个资源的访问权限
  • 获取许可(Acquire):当一个线程需要访问一个资源时,它必须首先从Semaphore获取一个或多个许可。如果Semaphore中有足够的许可,则获取成功,线程可以继续执行;否则,线程将被阻塞,直到有可用的许可为止。
  • 释放许可(Release):当线程完成对资源的访问后,它应该释放先前获取的许可,以便其他线程可以访问该资源。

Semaphore使用场景

  • 资源池限制:例如,一个系统可能有10个数据库连接,而多个线程可能同时需要访问这些连接。使用Semaphore可以确保在任何时候,最多只有10个线程可以访问数据库连接。
  • 并发限制:有时,你可能希望限制同时执行特定操作的线程数量。例如,你可能希望同时只有5个线程可以访问某个API或执行某个计算密集型任务。

Semaphore示例

下面是一个简单的示例,展示了如何使用Semaphore来限制对资源池的并发访问:

import java.util.concurrent.Semaphore;  
  
public class SemaphoreExample {  
    private final Semaphore semaphore;  
    private final int maxConcurrentAccess;  
  
    public SemaphoreExample(int maxConcurrentAccess) {  
        this.maxConcurrentAccess = maxConcurrentAccess;  
        this.semaphore = new Semaphore(maxConcurrentAccess);  
    }  
  
    public void accessResource() throws InterruptedException {  
        // 获取一个许可  
        semaphore.acquire();  
        try {  
            // 访问资源的代码...  
            System.out.println("Accessing resource. Remaining permits: " + semaphore.availablePermits());  
            // 模拟资源访问的耗时操作  
            Thread.sleep(1000);  
        } finally {  
            // 释放许可  
            semaphore.release();  
        }  
    }  
  
    public static void main(String[] args) {  
        SemaphoreExample example = new SemaphoreExample(3);  
        for (int i = 0; i < 5; i++) {  
            new Thread(() -> {  
                try {  
                    example.accessResource();  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }).start();  
        }  
    }  
}

可以看到, 在这个示例中,我们创建了一个Semaphore对象,其初始许可数量为3。然后,我们启动了5个线程来模拟对资源的并发访问。由于Semaphore的限制,在任何时候最多只有3个线程可以访问资源。

CountDownLatch (计数器/闭锁)

CountDownLatch 介绍

CountDownLatch 是 Java 并发工具包 java.util.concurrent 中的一个类,它允许一个或多个线程等待其他线程完成一组操作。CountDownLatch 的主要应用场景是协调多个线程,以便它们能够彼此等待,直到某个条件(一组操作的完成)被满足

CountDownLatch 基本概念

  • 计数器(Count):CountDownLatch 包含一个计数器,该计数器在创建 CountDownLatch
    对象时被初始化为给定的值。
  • 等待(Await):线程可以调用 await() 方法等待,该方法会阻塞线程,直到计数器达到零。
  • 计数(Count Down):当一个或多个线程完成了某个任务后,它们可以调用 countDown() 方法来减少计数器的值。

CountDownLatch 使用场景

  • 并行计算:你可以启动多个线程进行并行计算,并在所有线程都完成计算后,主线程继续执行后续操作。
  • 资源初始化:在应用程序启动时,可能需要加载或初始化一些资源。你可以使用多个线程并行加载资源,并在所有资源都加载完成后,主线程继续执行。

CountDownLatch 基本方法

  • CountDownLatch(int count):创建一个 CountDownLatch 实例,并设置计数器的初始值。
  • countDown():将计数器的值减一。如果计数器的值变为零,那么所有因调用 await() 方法而等待的线程将被唤醒。
  • await():使当前线程等待,直到计数器达到零。如果计数器已经是零,那么该方法立即返回。否则,线程将处于休眠状态,直到计数器变为零。
  • await(long timeout, TimeUnit unit):使当前线程等待,直到计数器达到零,或者等待时间超过了指定的超时时间。
  • getCount():返回计数器的当前值。

CountDownLatch 示例

  1. 某一线程在开始运行前等待 n 个线程执行完毕
    CountDownLatch 的计数器初始化为 n (new CountDownLatch(n)),每当一个任务线程执行完毕,就将计数器减 1 (countdownlatch.countDown()),当计数器的值变为 0 时,在 CountDownLatch 上 await() 的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。
@Test
void name() throws InterruptedException {
    //需求 : 有T1,T2,T3三个线程, 希望T1,T2,T3 按照顺序执行  join方法
    //需求 : T1,T2,T3执行完毕之后, 再执行主线程  CountDownLatch
    //线程计数器
    CountDownLatch latch = new CountDownLatch(3);

    Thread t1 = new Thread(() -> {
        try {
            Thread.sleep(10000);
            log.info("t1线程开始执行--------------");
            latch.countDown();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    Thread t2 = new Thread(() -> {
        try {
            Thread.sleep(20000);
            log.info("t2线程开始执行--------------");
            latch.countDown();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    Thread t3 = new Thread(() -> {

        try {
            Thread.sleep(30000);
            log.info("t3线程开始执行--------------");
            latch.countDown();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    t1.start();
    t2.start();
    t3.start();

    latch.await();
    log.info("主线程执行-------");
}
  1. 实现多个线程开始执行任务的最大并行性
    注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的 CountDownLatch 对象,将其计数器初始化为 1 (new CountDownLatch(1)),多个线程在开始执行任务前首先 coundownlatch.await(),当主线程调用 countDown() 时,计数器变为 0,多个线程同时被唤醒
@Test
void name() throws InterruptedException {
    //需求 : 有T1,T2,T3三个线程, 希望T1,T2,T3 按照顺序执行  join方法
    //需求 : T1,T2,T3执行完毕之后, 再执行主线程  CountDownLatch
    //线程计数器
    CountDownLatch latch = new CountDownLatch(1);
    //RCountDownLatch latch = redissonClient.getCountDownLatch("countdown");
    //latch.trySetCount(3);

    Thread t1 = new Thread(() -> {
        try {
            latch.await();
            Thread.sleep(1000);
            log.info("t1线程开始执行--------------");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    Thread t2 = new Thread(() -> {
        try {
            latch.await();
            Thread.sleep(2000);
            log.info("t2线程开始执行--------------");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    Thread t3 = new Thread(() -> {
        try {
            latch.await();
            Thread.sleep(3000);
            log.info("t3线程开始执行--------------");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    t1.start();
    t2.start();
    t3.start();

    log.info("主线程执行-------");
    latch.countDown();
    Thread.sleep(5000);
}

CyclicBarrier(循环栅栏)

CyclicBarrier介绍

CountDownLatch 是 Java 并发工具包 java.util.concurrent 中的一个类,它允许一个或多个线程等待其他线程完成一组操作。CountDownLatch 的主要应用场景是协调多个线程,以便它们能够彼此等待,直到某个条件(一组操作的完成)被满足。

CyclicBarrier基本概念

  • 计数器(Count):CountDownLatch 包含一个计数器,该计数器在创建 CountDownLatch
    对象时被初始化为给定的值。
  • 等待(Await):线程可以调用 await() 方法等待,该方法会阻塞线程,直到计数器达到零。
  • 计数(Count Down):当一个或多个线程完成了某个任务后,它们可以调用 countDown() 方法来减少计数器的值。

CyclicBarrier使用场景

  • 并行计算:你可以启动多个线程进行并行计算,并在所有线程都完成计算后,主线程继续执行后续操作。
  • 资源初始化:在应用程序启动时,可能需要加载或初始化一些资源。你可以使用多个线程并行加载资源,并在所有资源都加载完成后,主线程继续执行。

CyclicBarrier基本方法

  • CountDownLatch(int count):创建一个 CountDownLatch 实例,并设置计数器的初始值。
  • countDown():将计数器的值减一。如果计数器的值变为零,那么所有因调用 await() 方法而等待的线程将被唤醒。
  • await():使当前线程等待,直到计数器达到零。如果计数器已经是零,那么该方法立即返回。否则,线程将处于休眠状态,直到计数器变为零。
  • await(long timeout, TimeUnit unit):使当前线程等待,直到计数器达到零,或者等待时间超过了指定的超时时间。
  • getCount():返回计数器的当前值。

CyclicBarrier示例(银行流水)

CyclicBarrier 可以用于多线程计算数据,最后合并计算结果的应用场景。比如我们用一个 Excel 保存了用户所有银行流水,每个 Sheet 保存一个帐户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个 sheet 里的银行流水,都执行完之后,得到每个 sheet 的日均银行流水,最后,再用 barrierAction 用这些线程的计算结果,计算出整个 Excel 的日均银行流水。可以看到当线程数量也就是请求数量达到我们定义的 5 个的时候, await() 方法之后的方法才被执行。
另外,CyclicBarrier 还提供一个更高级的构造函数 CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障时,优先执行 barrierAction,方便处理更复杂的业务场景。示例代码如下:

import java.util.ArrayList;  
import java.util.List;  
import java.util.concurrent.BrokenBarrierException;  
import java.util.concurrent.CyclicBarrier;  
  
public class BankTransactionProcessor {  
  
    private static final int NUM_THREADS = 5; // 假设有5个线程用于处理不同的sheet  
  
    public static void main(String[] args) {  
        // 创建一个CyclicBarrier,当所有线程处理完各自的sheet后,执行barrierAction  
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUM_THREADS, () -> {  
            // 这里是barrierAction,当所有sheet都处理完成后,执行这个操作来合并结果  
            List<Double> dailyAverages = new ArrayList<>();  
            // 假设dailyAverages是从各个线程中收集的  
            // 在这里计算整个Excel的日均银行流水  
            double totalAverage = dailyAverages.stream().mapToDouble(Double::doubleValue).average().orElse(0);  
            System.out.println("Total daily average bank transaction: " + totalAverage);  
        });  
  
        // 模拟处理Excel sheet的线程  
        for (int i = 0; i < NUM_THREADS; i++) {  
            final int sheetIndex = i;  
            new Thread(() -> {  
                try {  
                    // 处理单个sheet的逻辑,假设得到每个sheet的日均银行流水  
                    double dailyAverage = processSheet(sheetIndex);  
                    // 这里可以假设有一个方式将dailyAverage加入到dailyAverages列表中  
                    // 但是在这个示例中,我们只是打印出来  
                    System.out.println("Sheet " + sheetIndex + " daily average bank transaction: " + dailyAverage);  
  
                    // 等待所有sheet处理完毕  
                    cyclicBarrier.await();  
                } catch (InterruptedException | BrokenBarrierException e) {  
                    e.printStackTrace();  
                }  
            }).start();  
        }  
    }  
  
    // 模拟处理单个sheet的逻辑  
    private static double processSheet(int sheetIndex) {  
        // 这里应该是读取sheet数据,计算日均银行流水的逻辑  
        // 为了示例简单,我们直接返回一个模拟值  
        return sheetIndex * 1000.0; // 假设每个sheet的日均银行流水是递增的  
    }  
}

CyclicBarrier 和 CountDownLatch 的区别

  • CountDownLatch 的实现是基于 AQS 的,而 CycliBarrier 是基于 ReentrantLock

  • CountDownLatch 是计数器,只能使用一次,而 CyclicBarrier 的计数器提供 reset 功能,可以多次使用。

  • 对于 CountDownLatch 来说,重点是“一个线程(多个线程)等待”,而其他的 N 个线程在完成“某件事情”之后,可以终止,也可以等待。而对于
    CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待。

  • CountDownLatch 是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而 CyclicBarrier 更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。

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

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

相关文章

新能源锂电池行业创业的财富方案,锂电池回收高阶课

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89292234 更多资源下载&#xff1a;关注我。 实战攻略 12年锂电池回收行业经验与坑全收录 课程内容&#xff1a; 001-课程介绍.mp4 002-锂电池的全种类认识.mp4 003-废品锂电池到级片粉末价值估算,mp…

Go微服务: Grpc服务注册在Consul的示例(非Go-Micro)

概述 现在&#xff0c;我们使用consul客户端的api来把GRPC服务实现注册到consul上&#xff0c;非Go-Micro的形式其实&#xff0c;consul官方提供了对应的接口调用来实现&#xff0c;golang中的consul/api包对其进行了封装我们使用consul/api来进行展示 目录结构 gitee.com/g…

纯CSS丝滑边框线条动画

在这个网站&#xff08;minimal-portfolio-swart.vercel.app&#xff09;发现一个不错的交互效果&#xff0c;用户体验效果很不错。如封面图所示&#xff0c;这个卡片上有一根白色的线条围绕着卡片移动&#xff0c;且在线条的卡片内部跟随这一块模糊阴影&#xff0c;特别是在线…

Execel 数据分析-如何使用筛选-图表-透视图-处理多变量数据集

如果你的数据有很多个变量&#xff0c;比如横轴X有a,b,c,d等几个变量&#xff0c;Y轴也有个变量&#xff0c;那么这时候就用得到。 比如下面的例子&#xff0c;测试GPU的kernel吞吐量&#xff0c;其中stream cnt&#xff0c;grid dim&#xff0c;block dim 产生后面几个变量&am…

如何给出好的“文言一心”指令?

一、文言一心是什么&#xff1f; 在现代技术背景下&#xff0c;“文言一心”还是百度公司创建的一款大语言模型。这款模型基于飞桨深度学习平台和文心知识增强大模型&#xff0c;并拥有强大的中文语料库&#xff0c;可以理解和生成富含文化内涵和哲理的文本内容。其核心技术架构…

第三方软件测试机构进行代码审计需要哪些专业的知识?

代码审计 进行代码审计需要专业的知识&#xff0c;包括编程语言、操作系统、数据库、网络知识以及安全知识等。 1.编程语言知识是进行代码审计的基础&#xff0c;因为你需要理解代码的语法和结构。对于不同的应用程序&#xff0c;你需要了解其所使用的编程语言的特点和语法规…

如何利用InputStream类实现文件读取与处理?

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

嵌入式UI开发-lvgl+wsl2+vscode系列:2、label(标签)+button(按钮)+slider(滑块)控件熟悉及其示例demo运行

文章目录 一、前言二、常见控件示例demo模拟环境运行及接口熟悉&#xff08;重要&#xff09;如何修改示例main函数测试各种示例1、label示例1.1、label示例1&#xff08;标签基础示例&#xff09;1.2、label示例2&#xff08;标签带阴影效果&#xff09;1.3、label示例3&#…

在微信公众号怎么添加留言板功能

在如今信息爆炸的时代&#xff0c;微信公众号已成为企业与用户互动的重要桥梁。如何在这个平台上脱颖而出&#xff0c;吸引用户的眼球&#xff0c;提升用户黏性&#xff0c;成为每一个公众号运营者都需要思考的问题。今天&#xff0c;我们就来聊聊如何在微信公众号中巧妙添加留…

新浪测试社招要个25K,第一次面大厂挂了

一面 1、讲下被测系统和你负责测试的模块功能&#xff1f; 2、为什么选择这个测试框架&#xff0c;这个测试框架有什么优缺点&#xff1f; 3、测试文件的目录&#xff0c;包含哪些包&#xff0c;这些之间是怎么调用的&#xff1f; 4、UI自动化和接口自动化都是怎么做的&…

【C语言】大小端字节序存储

引子 不知道你是否像我一样好奇过一个问题&#xff1a;为什么每当我们在调试查看内存窗口时&#xff0c;&#xff08;以int类型为例&#xff09;4个字节内容存储的顺序好像是倒着的。 比如下面这张图&#xff0c;十进制数2077转换为十六进制是0x81d&#xff0c;四个字节分别是…

Windows 11 HBuilder X的安装和环境搭建教程

文章目录 目录 文章目录 安装流程 小结 概要安装流程技术细节小结 概要 HBuilder X是一个由DCloud推出的集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要用于构建基于HTML、CSS和JavaScript的跨平台应用程序&#xff0c;如微信小程序、App、H5等。它提供了丰富的功能…

【iOS】Block总结

文章目录 前言一、Block如何捕获外界变量1.捕获自动变量2.捕获静态局部变量3.全局、全局静态变量 二、__block修饰符三、Block的类型四、判断block存储在哪里五、Block的copy操作六、源码分析Block_copy()七、__block 与 __forwarding八、block发生copy的时机总结 前言 之前的…

鹏哥C语言复习——程序的编译、链接和预处理

目录 可执行程序的生成&#xff1a; 预处理&#xff08;预编译&#xff09;&#xff1a; 预定义符号&#xff1a; #define&#xff08;重难点&#xff09;&#xff1a; 第一种的讲解&#xff08;定义常量&#xff09;&#xff1a; 第二种的讲解&#xff08;定义宏&#x…

怎么添加微信留言板功能

在这个信息爆炸的时代&#xff0c;如何让自己的微信公众号或朋友圈内容脱颖而出&#xff0c;成为每位内容创作者思考的问题。今天&#xff0c;我将为您揭示一种新颖且实用的功能——微信留言板&#xff0c;并带您探讨如何通过巧妙设置&#xff0c;将其打造成独一无二的主题&…

RuntimeError: CUDA out of memory. Tried to allocate 1.77 GiB?如何解决

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

JavaWeb-JS

目录 学习重点 什么是 JavaScript? Web标准 JS的引入方式 JS的基本语法 JS的函数 JS的对象 JS事件监听 学习重点 js 引入方式 js 基础语法 js 函数 js 对象 js 事件监听 什么是 JavaScript? Web标准 Web 标准也称为网页标准 &#xff0c;由一系列的标准组成&#xff0…

Spring - Spring Cache 缓存注解这样用,实在是太香了!

作者最近在开发公司项目时使用到 Redis 缓存&#xff0c;并在翻看前人代码时&#xff0c;看到了一种关于 Cacheable 注解的自定义缓存有效期的解决方案&#xff0c;感觉比较实用&#xff0c;因此作者自己拓展完善了一番后分享给各位。 Spring 缓存常规配置 Spring Cache 框架给…

GpuMall智算云:AUTOMATIC1111/stable-diffusion-webui/stable-diffusion-webui-v1.8.0

配置环境介绍 目前平台集成了 Stable Diffusion WebUI 的官方镜像&#xff0c;该镜像中整合如下资源&#xff1a; GpuMall智算云 | 省钱、好用、弹性。租GPU就上GpuMall,面向AI开发者的GPU云平台 Stable Diffusion WebUI版本&#xff1a;v1.8.0 Python版本&#xff1a;3.10.…

运维专题.Docker功能权限(Capabilities)管理和查看

运维专题 Docker功能权限&#xff08;Capabilities&#xff09; - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:htt…