面试知识库
中 基础

Semaphore使用场景#

一句话答案#

Semaphore 是计数信号量,通过 acquire/release 控制同时访问资源的线程数量,适用于限流和连接池场景。

核心要点

Semaphore 的原理:

  • 基于 AQS 共享模式:state = 剩余许可数
  • acquire():CAS 把 state 减 1(acquire(n) 减 n),不够就入 AQS 队列 park
  • release():CAS 把 state 加回去,并唤醒队列中等待的线程
  • 公平 / 非公平:new Semaphore(n, true) 公平(先看队列有没有人排队),默认非公平(直接 CAS 抢)
  • 许可不和线程绑定:A 线程 acquire、B 线程 release 也可以,所以不是可重入锁,也不会检查「谁持有」

常用 API:

  • acquire() / acquire(n):阻塞获取,可响应中断
  • tryAcquire() / tryAcquire(timeout, unit):拿不到立即返回或超时返回 false,适合「拿不到就降级」
  • acquireUninterruptibly():不响应中断
  • release() / release(n)、availablePermits()

典型用法(控制并发数):

典型场景:

  • 数据库等共享资源的并发访问上限
  • 调下游接口的并发数限制(配合 tryAcquire(timeout) 超时降级)
  • 有限资源池(如固定数量的打印机、许可证)
  • permit=1 时当作互斥锁用(不可重入)

三者对比:

维度CountDownLatchCyclicBarrierSemaphore
等待方式一个线程等待其他 N 个线程N 个线程互相等待控制并发数量
是否可重用否(一次性)是(自动重置)是
计数器方向递减到 0 触发从 parties 递减到 0 触发(然后自动重置)获取/释放许可
底层实现AQS(共享锁)ReentrantLock + ConditionAQS(共享锁)
典型场景主线程等子任务完成多阶段并行计算资源并发数限制

面试回答(2分钟版)

Semaphore 是基于 AQS 实现的计数信号量,通过 acquire 获取许可、release 释放许可来控制同时访问某个资源的线程数量。构造时指定许可总数 N,每个线程 acquire 成功则许可减一,许可耗尽后其他线程阻塞等待,直到有线程 release 归还许可。典型使用场景包括数据库连接池限流、接口并发数控制等。和 CountDownLatch、CyclicBarrier 的区别在于:CountDownLatch 是一次性的倒计时门闩,一个线程等 N 个子任务完成;CyclicBarrier 是可重用的栅栏,N 个线程互相等待全部到达后同时继续;Semaphore 是控制并发资源数量,许可可以反复获取释放。底层都基于 AQS,CountDownLatch 和 Semaphore 用共享模式,CyclicBarrier 用 ReentrantLock 加 Condition 实现。使用 Semaphore 时注意在 finally 中 release 防止泄漏,permit 设为 1 时就退化成互斥锁。支持公平和非公平模式,公平模式按等待顺序获取许可,非公平模式允许插队吞吐更高。

追问与易错

追问方向:

  • “Semaphore 能实现互斥锁吗?”→ 可以,permit 设为 1 就退化为互斥锁,但不支持可重入,通常用 ReentrantLock 更合适
  • “公平和非公平的区别?”→ 公平模式按等待队列顺序获取许可,保证不饥饿但吞吐低;非公平模式允许插队,吞吐更高但可能饥饿,默认非公平
  • “限流用 Semaphore 还是令牌桶?”→ Semaphore 适合控制并发数(如连接池),令牌桶适合控制请求速率(QPS);两者维度不同,单机并发控制用 Semaphore,接口限速用 Guava RateLimiter

易错点:

  • ❌ Semaphore 只能用于限流——还能用于资源池
  • ❌ 忘记在 finally 中 release