JUC框架(Semaphore、CountDownLatch、CyclicBarrier)

在这里插入图片描述

文章目录

    • 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 更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用PhpStudy在本地电脑搭建WordPress网站教程(2024版)

对新手来说&#xff0c;明白了建站3要素后&#xff0c;如果直接购买域名、空间去建站&#xff0c;因为不熟练&#xff0c;反复测试主题、框架、插件等费时费力&#xff0c;等网站建成可能要两三个月&#xff0c;白白损失这段时间的建站费用。那么新手怎么建测试网站来练手呢&am…

Redis使用Set实现点赞功能

文章目录 set 数据类型介绍不排序实现排序实现 set 数据类型介绍 Redis中的set类型是一组无序的字符串值。 set通过其独特的数据结构和丰富的命令提供了在存储和处理集合元素方面的一些非常有用的功能。下面列出了主要的set类型命令&#xff1a; SADD key member1 [member2]&a…

Docker 常用命令大全!!

Docker 常用命令 一、启动类1. 启动 docker2. 关闭 docker3. 重新启动 docker4. docker 设置自启动5. 查看 docker 运行状态6. 查看 docker 版本号等信息7. docker 帮助 二、 镜像类1. 查看镜像2. 搜索镜像3. 拉取镜像4. 运行镜像5. 删除镜像6. 加载镜像7. 保存镜像 三、容器类…

Java 类加载过程和双亲委派模型

Java 类加载过程概述 在 Java 中&#xff0c;类装载器把一个类装入 Java 虚拟机中&#xff0c;要经过三个步骤来完成&#xff1a;装载、链接和初始化&#xff0c;其中链接又可以分成校验、准备、解析 Java类加载过程分为如下步骤&#xff1a; 1.装载&#xff08; 加载&#xf…

Go 语言简介 -- 高效、简洁与现代化编程的完美结合

在现代软件开发领域&#xff0c;选择合适的编程语言对于项目的成功至关重要。Go 语言&#xff08;又称 Golang &#xff09;自 2009 年由Google发布以来&#xff0c;以其简洁的语法、高效的并发模型以及强大的性能&#xff0c;迅速成为开发者们的新宠。Go语言不仅融合了传统编译…

统计每个活动的用户访问量,且每个用户仅统计一次

场景&#xff1a;统计每个活动的用户访问量&#xff0c;且每个用户仅统计一次。 首先活动表是已经存在了的&#xff0c;一般情况下&#xff0c;我们都会在创建一个用户访问表&#xff0c;其中唯一主键是用户ID活动ID作为唯一主键 create table user_visist_activity_record(i…

Latex:newcommand

参考文献&#xff1a; latex中自定义的命令———\newcommand-CSDN博客LaTeX技巧924&#xff1a;详解newcommand的参数和默认值 - LaTeX工作室 (latexstudio.net) 文章目录 (re)newcommand自定义的一些命令 (re)newcommand ”定义命令“ 的定义&#xff1a; \newcommand{<…

GTX IP生成及参数详解(高速收发器九)

如下图所示&#xff0c;在IP Catalog中搜索gt&#xff0c;然后双击7 Series FPGAs Transcelvers Wizard打开IP配置界面。 图1 打开7 Series FPGAs Transcelvers Wizard IP 1、配置GT Selection界面 首先进入GT Selection配置界面&#xff0c;这个界面主要关注红框部分。从前文对…

AOP、注解、EL表达、若依权限,Security原理综合分析

AOP、注解、EL表达、若依权限&#xff0c;Security原理综合分析 案例一&#xff1a;更新、创建增强 需求产生 每个表中均有创建时间、创建人、修改时间、修改人等字段。 在操作时候手动赋值&#xff0c;就会导致编码相对冗余、繁琐&#xff0c;那能不能对于这些公共字段在某…

Vue3学习-用 vite@latest 初始化项目后,遇到无法识别 .vue 文件

引入app界面遇到 我的解决方案 1.根目录创建 env.d.ts&#xff0c;添加 declare module "*.vue" {import type { DefineComponent } from "vue"const vueComponent: DefineComponent<{}, {}, any>export default vueComponent }2.在 tsconfig.json…

iCloud 照片到 Android 指南:帮助您快速将照片从 iCloud 传输到安卓手机

​ 概括 iOS 和 Android 之间的传输是一个复杂的老问题。将 iCloud 照片传输到 Android 似乎是不可能的。放心。现在的高科技已经解决了这个问题。尽管 Apple 和 Android 不提供传输工具&#xff0c;但您仍然有其他有用的选项。这篇文章与您分享了 5 个技巧。因此&#xff0c;…

⌈ 传知代码 ⌋ 实现沉浸式交互故事体验

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

左极限与右极限

左极限与右极限 1. 前言 极限描述了函数在一个定点附近的行为&#xff0c;具体说就是当函数的自变量&#xff08;例如 x x x&#xff09;趋近于某一个值时&#xff0c;函数的因变量&#xff08;例如 y y y&#xff09;会产生什么样的特性&#xff08;或结果&#xff09;。 …

降价!免费!AI大模型开启价格战,企业如何“薅”出绿色财富?

近期&#xff0c;国内大模型技术供应商之间的价格战&#xff0c;使得这项原本成本较高的技术变得更加亲民&#xff0c;极大降低了企业的技术采用门槛。这不仅为企业提供了经济实惠的技术解决方案&#xff0c;更为他们的绿色低碳转型之路带来了新的机遇。 随着全球气候变化问题…

社区矫正程序管理端和小程序(支持人脸识别)

社区矫正作为我国刑事处罚执行方式中独特的种类&#xff0c;从2003年进行试点至今已有近20年的时间&#xff0c;在罪犯改造方面取得了突出成就&#xff0c;在法治国家建设过程中具有十分重要的意义。相较于监狱内服刑的执行方式&#xff0c;社区矫正更加侧重于对服刑人员进行教…

力扣刷题--747. 至少是其他数字两倍的最大数【简单】

题目描述 给你一个整数数组 nums &#xff0c;其中总是存在 唯一的 一个最大整数 。 请你找出数组中的最大元素并检查它是否 至少是数组中每个其他数字的两倍 。如果是&#xff0c;则返回 最大元素的下标 &#xff0c;否则返回 -1 。 示例 1&#xff1a; 输入&#xff1a;n…

AI+低代码,打通企业大模型应用最后一公里!

一、AI的趋势与发展 一夜之间&#xff0c;微软的AI全宇宙似乎已成型。 5月22日凌晨&#xff0c;在一年一度的2024微软Build大会上&#xff0c;微软CEO萨蒂亚纳德拉一口气宣布了50多项AI能力更新&#xff0c;涵盖GPT-4o上云、自研Cobalt芯片、团队版Copilot、SOTA小模型等。 此…

element el-table表格表头某一列表头文字或者背景修改颜色

效果如下 整体代码 &#xff0c;具体方法在最下面&#xff01; <el-table v-loading"listLoading" :data"sendReceivList" element-loading-text"Loading" border fit ref"tableList" :header-cell-class-name"addClass&quo…

大模型应用之基于Langchain的测试用例生成

一 用例生成实践效果 在组内的日常工作安排中&#xff0c;持续优化测试技术、提高测试效率始终是重点任务。近期&#xff0c;我们在探索实践使用大模型生成测试用例&#xff0c;期望能够借助其强大的自然语言处理能力&#xff0c;自动化地生成更全面和高质量的测试用例。 当前…

延迟重平衡优化(Deferred Re-balancing Optimization Schedule)

DRW 论文代码 elif args.train_rule DRW:train_sampler Noneidx epoch // 160betas [0, 0.9999]effective_num 1.0 - np.power(betas[idx], cls_num_list)print(f"\neffective_num:{effective_num}")per_cls_weights (1.0 - betas[idx]) / np.array(effective…