一文彻底弄懂JUC工具包的Semaphore

news/2024/11/13 11:28:45/文章来源:https://www.cnblogs.com/lgx211/p/18538270

Semaphore 是 Java 并发包 (java.util.concurrent) 中的重要工具,主要用于控制多线程对共享资源的并发访问量。它可以设置“许可证”(permit)的数量,并允许指定数量的线程同时访问某一资源,适合限流、资源池等场景。下面从源码设计、底层原理、应用场景、以及与其它 JUC 工具的对比来详细剖析 Semaphore。

一、Semaphore 的基本原理

Semaphore 本质上是一种计数信号量,内部维护一个许可计数,每个线程在进入时需要申请一个许可(acquire),完成后释放该许可(release)。当许可计数为零时,其他线程会阻塞,直到有线程释放许可。

1. 计数和许可

  • 许可数:Semaphore 在初始化时设置许可数,通常表示可以同时访问资源的线程数量。
  • 计数增减:调用 acquire() 时减少许可数,调用 release() 时增加许可数。
  • 公平性:Semaphore 可以设置为“公平”模式,保证线程按 FIFO 顺序获得许可。默认是“非公平”模式,性能更高,但线程获取许可的顺序不保证。

二、底层实现与源码解析

Semaphore 基于 AbstractQueuedSynchronizer (AQS) 实现,其实现方式和 CountDownLatch 类似,但使用了 AQS 的共享模式,并对许可计数进行精确管理。

1. AQS 的共享模式

Semaphore 使用 AQS 的共享模式(Shared),其中 AQS 的 state 表示剩余许可数。acquireShared() 方法用于申请许可,releaseShared() 方法用于释放许可。

2. 内部类 Sync 的实现

Semaphore 的核心实现依赖于其内部类 Sync,Sync 是 AbstractQueuedSynchronizer 的子类。根据是否是公平模式,有两种实现:NonfairSyncFairSync

  • NonfairSync:非公平模式。线程调用 acquire 时直接尝试获取许可,不保证顺序。
  • FairSync:公平模式。线程获取许可遵循队列顺序。
abstract static class Sync extends AbstractQueuedSynchronizer {Sync(int permits) {setState(permits);  // 设置初始许可数}final int getPermits() {return getState();}final int nonfairTryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 || compareAndSetState(available, remaining))return remaining;}}protected final boolean tryReleaseShared(int releases) {for (;;) {int current = getState();int next = current + releases;if (compareAndSetState(current, next))return true;}}
}
  • 非公平模式(nonfairTryAcquireShared:直接尝试获取许可,如果成功则更新 state
  • 公平模式(FairSync:遵循 AQS 的队列顺序,确保 FIFO 访问。

三、Semaphore 的使用方法

Semaphore 提供了三种主要方法来操作许可:

  • acquire():获取一个许可,若没有许可则阻塞。
  • release():释放一个许可,唤醒等待的线程。
  • tryAcquire():尝试获取许可,但不阻塞。
Semaphore semaphore = new Semaphore(3); // 初始许可数为 3// 获取许可,若无可用许可则阻塞
semaphore.acquire();      // 释放许可
semaphore.release();// 尝试获取许可,若不可用立即返回 false,不会阻塞
if (semaphore.tryAcquire()) {// do something
}

acquire() 源码

public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}

acquireSharedInterruptibly(1) 会调用 nonfairTryAcquireShared(1)tryAcquireShared(公平模式),尝试获取许可,若没有足够许可则阻塞等待。

release() 源码

public void release() {sync.releaseShared(1);
}

releaseShared(1) 增加许可数并唤醒阻塞线程,使等待线程得以继续执行。

四、Semaphore 的应用场景

Semaphore 非常适合控制对有限资源的访问,典型的应用场景有:

1. 连接池限流

在数据库连接池中,可以使用 Semaphore 限制同时访问连接的线程数。例如,数据库连接数有限,通过 Semaphore 控制同时访问的线程数量,避免过度负载。

public class DatabaseConnectionPool {private static final int MAX_CONNECTIONS = 5;private final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS);public Connection getConnection() throws InterruptedException {semaphore.acquire();return acquireConnection();}public void releaseConnection(Connection conn) {releaseConnection(conn);semaphore.release();}
}

2. 控制线程并发数

在高并发任务中,Semaphore 可限制同时执行的线程数量,例如,控制下载任务的并发数,避免过多线程导致的系统负载过高。

3. 资源分配和限流

Semaphore 适合资源共享场景,如共享打印机、固定线程数的线程池等,确保资源不会被过度占用。

五、与其他 JUC 工具的对比

1. CountDownLatch

  • 许可机制:Semaphore 的许可数可以动态变化,而 CountDownLatch 的计数器只能递减。
  • 适用场景:Semaphore 控制并发资源访问数量,而 CountDownLatch 用于线程等待。

2. CyclicBarrier

  • 作用:Semaphore 适合控制资源访问并发数,而 CyclicBarrier 则用于线程间的同步点,使所有线程达到某个屏障时一起继续。
  • 灵活性:Semaphore 更灵活,可随时 release(),不必等待所有线程。

3. ReentrantLock

  • 模式:ReentrantLock 是排他锁,每次只允许一个线程访问。Semaphore 允许多个线程共享资源(多个许可)。
  • 适用场景:ReentrantLock 适合独占资源场景,Semaphore 适合资源池、限流等。

4. Phaser

  • 复杂度:Phaser 用于复杂的多阶段同步,可以动态增加/减少线程,而 Semaphore 主要用于固定数量资源控制。
  • 适用性:Phaser 适合分阶段任务同步,而 Semaphore 适合控制资源并发数。

庐山烟雨浙江潮,未至千般恨不消。

到得还来别无事,庐山烟雨浙江潮。

近半年不会再更新了,居家办公的日子要结束了。读书,世界就在眼前;不读书,眼前就是世界。与你共勉!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

权限系统:一文搞懂功能权限、数据权限

大家好,我是汤师爷~ 在权限系统中,权限通常分为两大类:功能权限和数据权限。这两种权限相辅相成,共同决定了用户在系统中可以执行哪些操作、访问哪些信息。 功能权限 1、功能权限是什么 当登录某个系统时,为什么有些功能按钮是灰色的,而有些页面甚至完全不可见?这正是功…

2024.10.30(Maven)

Main放源代码 test放测试代码 pom.xml项目核心配置文件 Maven的主要功能有: 1.提供了一套标准化的项目结构 2.听了一套标准化的构建流程(编译、测试、打包、发布....) 3.提供了一套依赖管理机制

【日记】你们两个熊孩子,不要叫我叔叔啊啊啊啊(1630 字)

正文今天上午起床,窗外地面湿了。瞬间不想出门吃饭了。挣扎了一会儿,还是带着伞出门了。毕竟昨天说了要出门剪头发。花了一个小时吧好像。指定的那个理发店还挺远。理发师都认识我了,笑了笑,问我怎么剪。我还是说只修一下刘海,不要遮住眉毛和眼睛就行,后面和侧边还要继续…

东商项目mysql实例库(dingding)增量备份的实现

一、钉钉库(dingding)实例全量备份 1.机器:cvlink-xxcc-prd1172.16.50.59cvlink-xxcc-prd2172.16.50.60备注:59是主库,60为从库,因为做了主从,所以两个数据库数据是一致的,直接对从库60的实例钉钉进行备份即可。2.对从库60的实例进行备份: 2.1 登录从库:2.2 进行备份…

安娜的档案(Anna’s Archive)官网最新镜像网站入口(实时更新)

安娜的档案(Anna’s Archive)是一个广受欢迎的在线资源平台,汇集了Z-Library、Library Genesis和Sci-Hub等资源,拥有超过2000万册书籍和9000万篇论文。该网站提供多种下载选项,包括快速、慢速、libgen、IPFS和Tor浏览器下载,以满足不同用户的网络和需求。用户可以无需注册…

STM32的内存(堆和栈)

一般来说单片机的内存指的是FLASH和RAM,当在程序中定义了全局变量、局部变量、只读变量等参数时都是会存放到对应的FLASH或者是RAM中。具体对单片机FLASH和RAM的介绍之后再写,这里只对单片机内存分配,对堆和栈以及变量的存储做一个梳理和记录。1、FLASH(0x0800 0000) FLAS…

使用Boost.asio与Boost.beast基于协程连接ws

目录目录 前言 准备工作 实现初始化io_context并监听信号 启动连接ws的线程并启动io_context 建立tcp链接(以下步骤皆位于ws函数中) ws握手 传输数据 效果总结前言 本文主要介绍一个使用Boost.asio和Boost.beast基于协程连接Websocket(ws)的方法。其中C++版本为20,Boost版本…

使用 Let’s Encrypt 免费申请泛域名 SSL 证书,并实现自动续期

使用 Let’s Encrypt 免费申请泛域名 SSL 证书,并实现自动续期 目录使用 Let’s Encrypt 免费申请泛域名 SSL 证书,并实现自动续期🛠️ 环境准备💡 什么是 Let’s Encrypt?🧠 Let’s Encrypt 证书颁发原理1. 域名验证2. 证书生成3. 证书安装4. 自动续期🛠️ Certbot…

Python爬虫学习笔记

目录基础篇:HTTP:HTTP请求:请求行:请求头:请求体:HTTP响应:状态行:响应头:响应体:Requests库:GET请求:POST请求:HTML:HTML网页结构:HTML标签:网页解析:Regular Expression:元字符:量词:正则表达式:Re解析:实战案例:Beautiful Soup:安装:成员属性/函数…

高级语言程序设计第6次作业

高级语言程序设计课程第六次个人作业 这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2024C 这个作业要求在哪里:https://edu.cnblogs.com/campus/fzu/2024C/homework/13303 学号:102300303 姓名:梁佳 第1题第2题第3题第4题第5题第6题第7题第8题第9题第10题第11…

【NX/UG】解决:使用重用库的零件,下次打开发现零件丢失问题

NX12.0: 1、找到文件->实用工具->用户默认设置2、基本环境->重用库->可重用组件->更改组件目录[Windows],将目录更改到当前文件工作目录即可-------------------------------------------------------------------------------------------------------------…

腾讯云云服务器数据迁移实战方案

前言 我在三年前购买的腾讯云服务器今年过期咯, 今年的腾讯云双十一活动也是给力优惠攻略极速观看: 刻不容缓腾讯云双十一活动羊毛攻略!!!-腾讯云开发者社区-腾讯云 极速通道购买: 腾讯云11.11上云拼团Go 经过上面的攻略我购买了一个 2h4g 和 4h8g 的云服务器, 我将 Mysql 、Re…