在Java里如何使用Semaphore控制并发访问数量_Java信号量机制解析

落宇小哥_9032

落宇小哥_9032

2026-02-21

717人浏览

原创

new Semaphore(1) 不等于 synchronized,因其不绑定线程、不可重入、无内存可见性保证,且需手动配对acquire/release;适合跨线程传递许可,而非简单互斥。

在java里如何使用semaphore控制并发访问数量_java信号量机制解析

为什么 new Semaphore(1) 不等于 synchronized

很多人以为 Semaphore 设为 1 就是“换个写法的锁”,其实不是。它不绑定线程——同一个线程可以 acquire() 多次(不释放的话会阻塞自己),而 synchronized 是可重入且自动绑定当前线程的。更关键的是:Semaphore 不提供内存可见性保证,除非你额外用 volatile 或同步块配合共享变量。

  • 要用 Semaphore 做互斥,得确保每次 acquire() 后必有对应 release(),推荐用 try-finally
  • 如果只是保护一段临界区且无需跨线程传递许可,synchronized 或 ReentrantLock 更安全、语义更清晰
  • Semaphore(1) 真正适合的场景是:需要在不同线程间“传递”进入权限(比如生产者获取许可后交给消费者去执行)

如何避免 acquire() 阻塞导致线程卡死

默认 acquire() 会无限期等待,一旦许可长期被占用(比如某线程抛异常没释放),整个等待队列就挂住了。必须用带超时的版本 + 显式错误处理。

  • 优先用 tryAcquire(long timeout, TimeUnit unit),超时返回 false,可做降级或告警
  • 不要在循环里裸调 acquire(),尤其在连接池、任务调度等长生命周期场景中
  • 如果业务允许“尽力而为”,可结合 tryAcquire()(无等待)快速失败,避免排队积压
if (sem.tryAcquire(3, TimeUnit.SECONDS)) {
    try {
        // 执行受控操作
    } finally {
        sem.release(); // 必须放 finally 里
    }
} else {
    // 走备用逻辑,比如返回错误码或进重试队列
}

公平模式(fair = true)带来的性能代价

new Semaphore(n, true) 会让等待线程按 FIFO 排队,看起来更“可控”,但实际会显著降低吞吐量——因为要维护队列结构、增加 CAS 竞争,尤其在高并发下,平均响应时间可能翻倍。

java-api-doc-generator
java-api-doc-generator

分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。

下载
  • 仅当业务强依赖“先到先得”语义时才开启公平模式(例如某些金融限流策略)
  • 绝大多数限流、资源池场景(如数据库连接数控制),用非公平模式(默认)更合适
  • 公平模式下 acquire() 的延迟波动更大,监控时容易误判为系统抖动

release() 被多调用一次会发生什么

Semaphore 允许 release() 次数超过初始许可数,这会导致许可数“虚高”。比如初始为 5,被多 release() 两次后变成 7,后续 7 个线程都能直接通过 —— 这不是 bug,是设计使然,但极易引发资源超卖。

  • 典型诱因:异常分支漏写 acquire(),但 release() 却执行了(比如在 finally 里无条件 release)
  • 解决方法:把 acquire() 和 release() 绑定在同一作用域,或用封装类(如 AutoCloseableSemaphore)强制配对
  • 没有运行时检查机制,只能靠代码审查和单元测试覆盖边界路径

Semaphore 的核心价值不在“加锁”,而在“定量配额”。它的危险点不在于难写,而在于错得安静——多一次 release(),少一次 acquire(),系统照常跑,直到流量高峰时连接被打满、线程池饿死,才暴露出来。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

java 并发访问

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
c++中volatile关键字的作用
c++中volatile关键字的作用

本专题整合了c++中volatile关键字的相关内容,阅读专题下面的文章了解更多详细内容。

2025.10.23

814

12

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

4558

6

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

2023.06.29

2965

3

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2023.08.14

4101

10

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

2023.08.31

3331

3

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.05

1047

5

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.09

2907

5

数据库对象名无效怎么解决
数据库对象名无效怎么解决

数据库对象名无效解决办法:1、检查使用的对象名是否正确,确保没有拼写错误;2、检查数据库中是否已存在具有相同名称的对象,如果是,请更改对象名为一个不同的名称,然后重新创建;3、确保在连接数据库时使用了正确的用户名、密码和数据库名称;4、尝试重启数据库服务,然后再次尝试创建或使用对象;5、尝试更新驱动程序,然后再次尝试创建或使用对象。

2023.10.16

2887

4

vb连接access数据库的方法
vb连接access数据库的方法

vb连接access数据库方法:1、使用ADO连接,首先导入System.Data.OleDb模块,然后定义一个连接字符串,接着创建一个OleDbConnection对象并使用Open() 方法打开连接;2、使用DAO连接,首先导入 Microsoft.Jet.OLEDB模块,然后定义一个连接字符串,接着创建一个JetConnection对象并使用Open()方法打开连接即可。

2023.10.16

2953

3

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习