在Java中Semaphore如何控制并发访问_Java信号量机制解析
发布时间 - 2026-02-02 00:00:00 点击率:次new Semaphore(1) 不等于 synchronized,因前者是基于AQS的可跨线程释放的共享锁,后者是JVM层必须同栈帧成对使用的独占锁;且Semaphore阻塞时线程状态为WAITING而非BLOCKED。
为什么 new Semaphore(1) 不等于 synchronized
因为 Semaphore 是基于 AQS 的共享锁,而 synchronized 是 JVM 层的独占锁;前者可跨线程、跨方法释放(比如 acquire 在线程 A,release 在线程 B),后者必须成对出现在同一个栈帧里。实际用错时常见现象是:线程卡死在 acquire(),但没任何线程调用 release() —— 尤其在异常路径下忘记 finally 释放。
实操建议:
- 永远把
acquire()放在try前,release()放在finally块里 - 避免用
new Semaphore(1)替代synchronized,除非你需要超时、中断响应或跨上下文释放 -
Semaphore构造函数第二个参数fair默认为false,非公平模式下可能造成线程饥饿,高一致性要求场景应显式设为true
acquire() 阻塞时线程状态到底是 WAITING 还是 BLOCKED
是 WAITING。因为 Semaphore 底层调用的是 LockSupport.park(),而非进入 monitor 锁竞争队列。这会影响线程 dump 分析:看到 java.lang.Thread.State: WAITING (parking) 就该想到可能是 Semaphore、CountDownLatch 或 Condition 导致的挂起,而不是锁竞争。
关键区别:
-
BLOCKED:在 synchronized 或ReentrantLock.lock()等待获取 monitor 或 AQS 同步队列头节点时出现 -
WAITING:调用acquire()、await()、join()等无超时阻塞方法后进入 - 若用了
acquireUninterruptibly(),即使被中断,线程仍保持WAITING,不会抛InterruptedException
如何安全地限制数据库连接池之外的外部资源访问
比如调用第三方 HTTP 接口,每秒最多 10 次请求。这时不能直接用连接池自身的限流(如 HikariCP 的 maximumPoolSize),而要用 Semaphore 包裹调用逻辑。
注意点:
- 信号量实例必须是共享的单例,不能每次请求都 new 一个
- 超时控制优先用
tryAcquire(long timeout, TimeUnit unit),避免无限等待拖垮整个服务 - 如果接口返回 429(Too Many Requests),应主动
release()并补偿令牌,否则下次请求仍会被拒绝 - 不要在
acquire()和实际调用之间插入耗时操作(如日志序列化、参数校验),否则信号量保护的时间窗口变大,失去限流意义
permits 数量设太大或太小会有什么实际影响
设太小(如 new Semaphore(2) 用于处理 100 QPS 的 API)会导致大量线程排队,getQueueLength() 持续增长,CPU 花在 AQS 队列维护上,吞吐反而下降;设太大(如 1000)则失去限流作用,还可能掩盖下游真实容量瓶颈。
更隐蔽的问题:
- 初始 permits 设为 0 可实现“冷启动开关”——后续用
release(n)动态放开,但要注意没有线程能acquire()成功,直到第一次release() -
availablePermits()返回的是当前剩余数,不是初始值,不能靠它判断是否“刚初始化” - 调用
drainPermits()会清空所有可用令牌并返回数量,适合做瞬时熔断,但之后必须配对release(n)才能恢
复,否则永久不可用
真正难的是根据下游 SLA(比如平均响应 200ms,错误率 getQueueLength() 和 hasQueuedThreads(),而不是拍脑袋定个数字。
# java
# 栈
# ai
# 区别
# 并发访问
# 为什么
# jvm
# 构造函数
# try
# 接口
# finally
# 线程
# Thread
# 并发
# 数据库
# http
# 的是
# 信号量
# 放在
# 令牌
# 设为
# 太大
# 而非
# 太小
# 不等于
# 而不是
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Python自然语言搜索引擎项目教程_倒排索引查询优化案例
Laravel如何配置和使用缓存?(Redis代码示例)
Linux后台任务运行方法_nohup与&使用技巧【技巧】
厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?
Laravel事件监听器怎么写_Laravel Event和Listener使用教程
手机网站制作平台,手机靓号代理商怎么制作属于自己的手机靓号网站?
DeepSeek是免费使用的吗 DeepSeek收费模式与Pro版本功能详解
html5如何实现懒加载图片_ intersectionobserver api用法【教程】
Laravel怎么配置不同环境的数据库_Laravel本地测试与生产环境动态切换【方法】
Laravel怎么生成URL_Laravel路由命名与URL生成函数详解
Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】
购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?
Laravel怎么清理缓存_Laravel optimize clear命令详解
Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理
敲碗10年!Mac系列传将迎来「触控与联网」双革新
如何实现建站之星域名转发设置?
C++用Dijkstra(迪杰斯特拉)算法求最短路径
zabbix利用python脚本发送报警邮件的方法
javascript事件捕获机制【深入分析IE和DOM中的事件模型】
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
Win11怎么设置虚拟桌面 Win11新建多桌面切换操作【技巧】
Win11怎样安装网易有道词典_Win11安装词典教程【步骤】
BootStrap整体框架之基础布局组件
laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法
如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?
常州企业网站制作公司,全国继续教育网怎么登录?
Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程
html5如何设置样式_HTML5样式设置方法与CSS应用技巧【教程】
如何为不同团队 ID 动态生成多个非值班状态按钮
浅谈redis在项目中的应用
Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区
为什么php本地部署后css不生效_静态资源加载失败修复技巧【技巧】
制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?
Android仿QQ列表左滑删除操作
ChatGPT回答中断怎么办 引导AI继续输出完整内容的方法
智能起名网站制作软件有哪些,制作logo的软件?
Laravel怎么发送邮件_Laravel Mail类SMTP配置教程
韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南
Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解
,怎么在广州志愿者网站注册?
今日头条微视频如何找选题 今日头条微视频找选题技巧【指南】
Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践
Laravel如何使用Service Container和依赖注入?(代码示例)
Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】
制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?
JavaScript如何操作视频_媒体API怎么控制播放
活动邀请函制作网站有哪些,活动邀请函文案?
如何在阿里云完成域名注册与建站?
再谈Python中的字符串与字符编码(推荐)
Laravel的.env文件有什么用_Laravel环境变量配置与管理详解


