Java 中 Semaphore 是什么?深入理解并发编程中的信号量

先给大家一个福利,2025年Java面试宝典已经整理好了: 链接: https://pan.baidu.com/s/1RUVf75gmDVsg8MQp4yRChg?pwd=9b3g 提取码: 9b3g
如果你在面试中被问到:“Java 中 Semaphore 是什么?” 别慌,这其实是考察你对并发编程工具掌握程度的经典问题。作为 Java 程序员,理解 Semaphore 是搞定高并发场景的必备技能。
🔧 一、Semaphore 的核心概念:控制访问的“许可证”
想象一下停车场:车位是有限的。Semaphore 就是管理这些“车位”(资源)的看门大爷。在 Java 并发包里,Semaphore 是一个计数器,用来控制同时访问某个特定资源的线程数量。
- 许可证(Permits): 你可以把
Semaphore想象成持有一定数量“许可证”的对象。线程想访问受保护的资源?行,但必须先拿到一个许可证。 - 核心操作:
acquire():线程调用这个方法尝试获取一个许可证。如果还有许可证可用(计数器 > 0),线程立刻获取成功,计数器减1。如果没有许可证了(计数器=0),线程就会被阻塞,直到有其他线程释放许可证或被打断。release():线程使用完资源后,调用这个方法归还许可证,计数器加1。这样等待的线程就有机会获取许可证了。
- 核心作用:
Semaphore最核心的作用就是限流。它确保同一时刻只有限定数量的线程能执行某段代码或访问某个资源池(比如数据库连接池、线程池、限流场景)。
⚙️ 二、Semaphore 的工作原理:计数器与同步队列

- 初始化: 创建
Semaphore时指定初始的许可证数量(比如new Semaphore(5)表示有5个许可证)。 - 线程获取(acquire):
- 当线程调用
acquire()时,Semaphore内部计数器检查。 - 如果计数器 > 0,计数器减1,线程立即获得许可,继续执行。
- 如果计数器 = 0,线程无法获得许可,会被放入一个等待队列中挂起(阻塞)。
- 当线程调用
- 线程释放(release):
- 当持有许可证的线程调用
release()时,Semaphore内部计数器加1。 Semaphore会检查等待队列。如果有线程在等待,它会唤醒队列中的一个(或多个,取决于公平策略)线程。被唤醒的线程尝试获取许可证(此时计数器会再次减1),成功则继续执行。
- 当持有许可证的线程调用
- 公平性: 创建
Semaphore时可以指定公平模式(new Semaphore(5, true))。在公平模式下,线程获取许可的顺序遵循 FIFO(先进先出),避免饥饿。非公平模式下性能更高,但可能出现后来的线程插队成功的情况。
🚀 三、Java 中 Semaphore 的典型应用场景
理解了 Semaphore 是什么,那它在 Java 开发中用在哪儿呢?面试时能说出这些场景,绝对加分:
- 资源池管理(如数据库连接池): 这是最经典的用法。假设连接池只有10个连接。用
Semaphore(10)控制,最多只允许10个线程同时获取连接。线程获取连接前acquire(),用完归还时release()。确保不会因为请求过多而耗尽连接。 - 限流/流量控制: 比如某个 API 接口需要限制每秒最多处理100个请求。可以初始化一个
Semaphore(100),每秒重置一次(或使用其他机制)。每个请求线程在处理前必须acquire()成功,否则拒绝或等待。有效防止系统被突发流量打垮。 - 生产者-消费者模型(有界缓冲区): 除了
BlockingQueue,Semaphore也能实现。用两个信号量:emptySlots(初始值为缓冲区大小) 控制生产者是否能放数据,fullSlots(初始值为0) 控制消费者是否能取数据。生产者放数据前acquire(emptySlots),放完后release(fullSlots);消费者取数据前acquire(fullSlots),取完后release(emptySlots)。 - 控制并发执行的任务数: 比如有个任务列表,你希望最多同时运行5个任务。用
Semaphore(5),每个任务线程启动前先acquire(),执行完后release()。这样就能轻松控制并发度。
🛠 四、Java 中使用 Semaphore 的关键点与技巧
在 Java 中使用 Semaphore 时,记住这些要点:
- 初始化:
Semaphore semaphore = new Semaphore(int permits);或Semaphore semaphore = new Semaphore(int permits, boolean fair); - 获取许可:
semaphore.acquire();// 阻塞直到获取一个许可semaphore.acquire(int n);// 阻塞直到获取n个许可semaphore.tryAcquire();// 尝试获取,立即返回true/false,不阻塞semaphore.tryAcquire(long timeout, TimeUnit unit);// 带超时的尝试获取
- 释放许可:
semaphore.release();// 释放一个许可semaphore.release(int n);// 释放n个许可
- 重要原则:
acquire和release通常需要配对使用,并且release一般要放在finally块中,确保无论任务是否正常完成或发生异常,许可证都能被释放,避免死锁或资源泄漏。 - 与锁的区别:
Semaphore不是锁!它不绑定特定资源,不记录持有者。它只关心“许可证”的数量。锁(如ReentrantLock)通常用于互斥,保证只有一个线程访问,而Semaphore允许多个线程(数量可控)同时访问。
💡 五、面试中关于 Semaphore 的回答思路
面试官问“Java 中 Semaphore 是什么”,别只干巴巴地说“是一个计数器”。按这个结构回答,清晰又专业:
- 一句话定义: “
Semaphore是 Java 并发工具包 (java.util.concurrent) 中的一个同步辅助类,它维护了一个许可证计数器,用于控制同时访问特定资源的线程数量。” - 核心机制: “线程通过调用
acquire()方法尝试获取许可证。如果计数器 >0,获取成功,计数器减1;如果计数器=0,线程被阻塞。线程使用完资源后调用release()归还许可证,计数器加1,并可能唤醒等待的线程。” - 核心目的: “它的主要目的是实现限流或管理对有限资源(如数据库连接、线程池任务槽)的并发访问。”
- 关键特性: “可以指定初始许可证数量和公平模式。
acquire和release操作是线程安全的。” - 应用举例: “比如,用
Semaphore实现一个简单的数据库连接池,或者限制某个高负载接口的并发请求数。” - 与锁的区别(可选,加分): “它不同于锁(如
ReentrantLock)。锁通常用于严格的互斥(一次一个线程),而Semaphore允许多个线程(数量可控)并发访问资源池。”
🎁 资源推荐与优惠
掌握并发编程是 Java 程序员面试的硬通货。除了理解 Semaphore 是什么,系统性地准备面试题至关重要。前面提到的 2025年Java面试宝典 资料非常全面:
链接: https://pan.baidu.com/s/1RUVf75gmDVsg8MQp4yRChg?pwd=9b3g 提取码: 9b3g

如果你需要开通 面试鸭会员 获取更多独家题库、模拟面试、专家答疑等深度服务,别忘了通过 面试鸭返利网 来找我!通过我这里下单购买面试鸭会员,可以享受 25 元现金返利,实实在在的优惠帮你降低求职成本。返利详情请访问: 面试鸭返利网 。
希望这篇文章帮你搞清楚了 Java 中 Semaphore 是什么,也祝你在下一次面试中顺利通关!用好并发工具,写出高效稳定的代码


