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

Semaphore和CountDownLatch

目录

1. Semaphore (信号量)

1.1 核心概念

1.2 实战中主要应用场景(设计思想)

停车场管理系统(数据库连接池类似):

2. CountDownLatch (倒计时门闩shuān‌)

2.1 核心概念

2.2 实战中主要应用场景(设计思想)

3. 核心区别总结

3.1 如何选择?


前言:

在 Java 并发编程的世界里,AbstractQueuedSynchronizer(简称 AQS)是 java.util.concurrent.locks 包下的一个核心抽象类。

           AQS,从名字看就知道是个跟队列、同步相关的抽象神器。简单来说,它为 Java 里实现锁和其他同步组件打造了一个超实用的基础框架。有了它,开发者能轻松定制各种复杂的同步需求,像是 ReentrantLock(可重入锁)、Semaphore(信号量)、CountDownLatch(倒计时器)这些大名鼎鼎的并发工具,底层都离不开 AQS 的强力支撑。

           和传统的 synchronized 关键字相比,AQS 的优势可不少。synchronized 用起来简单直接,编译器编译后会在同步块前后生成 monitorenter 和 monitorexit 字节码指令,靠对象头里的标记来控制锁的获取和释放。但它灵活性欠佳,像一些复杂的同步场景就有点应付不来。AQS 则不同,它把同步状态、线程排队这些底层逻辑都封装得妥妥当当,开发者可以根据需求定制同步规则,实现更精细的并发控制,而且在性能优化上也有更多的施展空间,能轻松应对高并发挑战。

       今天重点讲述Semaphore和CountDownLatch,具体如下:

1. Semaphore (信号量)

1.1 核心概念

Semaphore 用来控制同时访问特定资源的线程数量,它通过维护一组“许可证”(permits)来实现。你可以把它想象成一个售票厅,只有固定数量的票(许可证)。拿到票的线程可以进入“场馆”(访问资源),用完后归还票,其他线程才能获取。

  • 主要方法

    • acquire(): 获取一个许可证。如果无法获取(许可证为0),则线程阻塞,直到有许可证可用或被中断。

    • release(): 释放一个许可证,将其返还给信号量,从而允许一个等待的线程获取它。

    • 还有其他方法如 tryAcquire()(尝试获取,不阻塞)、acquire(int permits)(获取多个)等。

1.2 实战中主要应用场景(设计思想)

它适用于流量控制,特别是那种资源有限(如数据库连接、线程池、带宽),需要限制同时使用资源的线程数量的场景。

停车场管理系统(数据库连接池类似):
package onlyqi.daydayupgo06.juc;import java.util.concurrent.Semaphore;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;// 停车场类
class ParkingLot {private final Semaphore semaphore;private final int capacity;private AtomicInteger atomicIntegerAvailableSpots = new AtomicInteger();public ParkingLot(int capacity) {this.capacity = capacity;this.semaphore = new Semaphore(capacity);atomicIntegerAvailableSpots.set(capacity);}// 车辆进入停车场public void enter(int carId) throws InterruptedException {// 获取一个车位,如果没有则等待semaphore.acquire();// 同步更新可用车位计数System.out.printf("车辆 %d 进入停车场,可用车位: %d/%d%n",carId, atomicIntegerAvailableSpots.decrementAndGet(), capacity);}// 车辆离开停车场public void exit(int carId) {// 同步更新可用车位计数System.out.printf("车辆 %d 离开停车场,可用车位: %d/%d%n",carId, atomicIntegerAvailableSpots.incrementAndGet(), capacity);// 释放一个车位semaphore.release();}
}// 车辆线程类
class Car extends Thread {private final int carId;private final ParkingLot parkingLot;private final Random random = new Random();public Car(int carId, ParkingLot parkingLot) {this.carId = carId;this.parkingLot = parkingLot;}@Overridepublic void run() {try {System.out.printf("车辆 %d 到达停车场%n", carId);// 尝试进入停车场parkingLot.enter(carId);// 在停车场内停留随机时间(1-3秒)int stayTime = random.nextInt(2000) + 1000; // 1000-3000毫秒Thread.sleep(stayTime);// 离开停车场parkingLot.exit(carId);} catch (InterruptedException e) {Thread.currentThread().interrupt();System.out.printf("车辆 %d 操作被中断%n", carId);}}
}// 主类
public class ParkingLotSimulation {public static void main(String[] args) {// 创建一个容量为5的停车场ParkingLot parkingLot = new ParkingLot(5);// 模拟10辆汽车int carCount = 10;Random random = new Random();for (int i = 1; i <= carCount; i++) {Car car = new Car(i, parkingLot);car.start();// 车辆到达时间间隔随机(100-500毫秒)try {Thread.sleep(random.nextInt(400) + 100);} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}}
}

2. CountDownLatch (倒计时门闩shuān‌)

2.1 核心概念

CountDownLatch 允许一个或多个线程等待其他线程完成操作。它像一个倒计时的计数器:初始化一个数值,线程通过 countDown() 方法将计数器减1,await() 方法会阻塞当前线程,直到计数器减到0。

  • 主要方法

    • await(): 使当前线程等待,直到计数器减到0。

    • countDown(): 将计数器减1。

    • await(long timeout, TimeUnit unit): 带超时的等待。

2.2 实战中主要应用场景(设计思想)

它适用于“主从”协作模式,一个或多个主线程需要等待所有准备工作(由其他从线程完成)就绪后,才能继续执行。或者用于让多个线程在同一时刻同时开始执行(类似于赛跑发令枪)。

并行计算,汇总结果(应用程序启动前的准备工作类似:
主线程需要等待所有必要的服务(如数据库、缓存、网络连接)都初始化完成后,才能对外提供服务。

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ParallelSum {public static void main(String[] args) throws InterruptedException {int taskCount = 5;CountDownLatch latch = new CountDownLatch(taskCount);List<Integer> results = new ArrayList<>();ExecutorService executor = Executors.newCachedThreadPool();Random random = new Random();// 提交并行任务for (int i = 0; i < taskCount; i++) {final int taskId = i;executor.execute(() -> {try {int val = random.nextInt(100); // 模拟计算结果Thread.sleep(random.nextInt(1000)); // 模拟计算耗时synchronized (results) { results.add(val); }System.out.printf("任务%d完成: %d%n", taskId, val);} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {latch.countDown();}});}// 等待所有任务完成并汇总结果latch.await();int sum = results.stream().mapToInt(Integer::intValue).sum();System.out.printf("汇总结果: 总和=%d, 平均值=%.1f%n", sum, sum / (double) taskCount);executor.shutdown();}
}

3. 核心区别总结

特性SemaphoreCountDownLatch
目的控制资源访问的线程数量 (流量控制)等待一个或多个事件完成 (线程协作)
计数器可增减acquire() 减1,release() 加1只减不增countDown() 减1,无法重置
重用性可以。许可证可以被获取和释放,循环使用。不可以。计数器到0后,门闩打开,无法重置(除非用新的实例)。
核心方法acquire(), release()await(), countDown()
典型比喻票闸机(有票才能进,出来还票)发令枪(所有人准备好,等一声枪响)或 终点线(等所有人都跑完)
3.1 如何选择?
  • 当你需要限制同时访问某个资源的线程数时,用 Semaphore

  • 当你需要一个或多个线程等待其他一系列操作完成后才能继续时,用 CountDownLatch


文章转载自:

http://R0Aaac2g.zcsyz.cn
http://0PFZmeza.zcsyz.cn
http://iNp1IyWx.zcsyz.cn
http://bt1OHFdw.zcsyz.cn
http://pV3zhuBB.zcsyz.cn
http://VDm94DXd.zcsyz.cn
http://69YmvBVr.zcsyz.cn
http://Hj3dMxFl.zcsyz.cn
http://Br6gXzzT.zcsyz.cn
http://mjaEOwtI.zcsyz.cn
http://bkxtot2R.zcsyz.cn
http://Y6CVCSgO.zcsyz.cn
http://IDnHuSf2.zcsyz.cn
http://2P79obMI.zcsyz.cn
http://MtIcWwpF.zcsyz.cn
http://Mts5gYXr.zcsyz.cn
http://CEcSTJgF.zcsyz.cn
http://VPnXiLty.zcsyz.cn
http://YN03Z5MC.zcsyz.cn
http://U1zSBY20.zcsyz.cn
http://UwFkcQwa.zcsyz.cn
http://hFg2nPtA.zcsyz.cn
http://RNTDxTEE.zcsyz.cn
http://qdUbIedF.zcsyz.cn
http://Ennov4pO.zcsyz.cn
http://AUFfAEYw.zcsyz.cn
http://7fWjTVdv.zcsyz.cn
http://S1bW66tA.zcsyz.cn
http://FuQf6NDp.zcsyz.cn
http://JRZMLAGB.zcsyz.cn
http://www.dtcms.com/a/380688.html

相关文章:

  • 实战ELK与AI MCP:构建高可用的智能化日志可观测体系
  • SAP-MM:SAP MM学习分享:深入浅出解析物料需求计划(MRP)及MRP配置图解
  • 【LLM】使用 Google ADK、Gemini、QDrant 和 MCP 构建深度研究系统
  • 【CSS学习笔记2】-css复合选择器
  • 186. Java 模式匹配 - Java 21 新特性:Record Pattern(记录模式匹配)
  • Electron下载失败
  • Origin绘制双Y轴网格叠加图|科研论文图表教程(附数据排列格式)
  • XXL-JOB框架SRC高频漏洞分析总结
  • 未启用Spring事务管理 执行mapper.xml文件的sql,为什么会自动提交
  • 亚马逊云代理:亚马逊云怎么样进行大规模数据分析与处理?
  • Linux防火墙iptables
  • 基于联邦学习与神经架构搜索的可泛化重建:用于加速磁共振成像|文献速递-最新医学人工智能文献
  • 如何将 Wine 应用包转换成玲珑格式包:完整技术教程
  • 函数库 动静态库
  • EPC企业如何通过数字化管理提高盈利能力?
  • P2678 [NOIP 2015 提高组] 跳石头
  • 旋转位置编码的论文阅读
  • UE5 基础应用 —— 08 - 动画蓝图 简单使用
  • unity pcd 二进制版 简单显示文件对象(单色)
  • 面试题:Redis要点总结(复制、哨兵、集群)
  • Leetcode 18 java
  • Redis集群为何采用16384个槽的设计?
  • 《树与二叉树详解:概念、结构及应用》
  • Certimate SSL证书自动申请部署
  • 《Spring事务的失效》
  • Maya绑定:小球挤压拉伸变形详细绑定(晶格、簇、测量工具、节点编辑器)
  • 【比亚迪璇玑架构深度解析:重新定义智能电动汽车的“整车智能”】
  • jdbc DAO封装及BaseDAO工具类
  • jajajajajajajava
  • 自动生成链接