面试知识库
基础

Semaphore使用场景#

一句话答案#

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

核心要点

CountDownLatch(倒计时门闩):

// 场景:主线程等待 N 个子任务全部完成后再继续

CountDownLatch latch = new CountDownLatch(3);  // 计数器 = 3

// 子任务完成后调用 countDown()
executor.submit(() -> { doTask1(); latch.countDown(); });
executor.submit(() -> { doTask2(); latch.countDown(); });
executor.submit(() -> { doTask3(); latch.countDown(); });

latch.await();  // 主线程阻塞,直到计数器减到 0
System.out.println("所有子任务完成");

// 特点:一次性的,计数器减到 0 后不能重置
// 典型场景:启动时等待多个服务初始化完成、并行查询多个数据源后汇总结果
java

CyclicBarrier(循环栅栏):

Semaphore(信号量):

三者对比:

维度CountDownLatchCyclicBarrierSemaphore
等待方式一个线程等待其他 N 个线程N 个线程互相等待控制并发数量
是否可重用否(一次性)是(自动重置)
计数器方向递减到 0 触发递增到 N 触发获取/释放许可
底层实现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