【锁3】Semaphore(信号量)

📅 2026/8/1 14:21:03 👁️ 阅读次数 📝 编程学习
【锁3】Semaphore(信号量)

Semaphore(信号量)的核心是用 N 张“许可证”限制同时访问资源的线程数——线程进来先 acquire() 拿一张,用完 release() 还回去;许可证发完了,后面的线程就得排队。下面给你 Java 最常用版本的可运行示例。

核心概念

初始化时指定许可证数量 N,相当于“最多允许 N 个线程同时进临界区”

acquire():拿许可证,没有就阻塞等待

release():还许可证,务必放在finally或使用with,否则忘了释放就会死锁

tryAcquire():非阻塞版本,拿不到立刻返回false,适合做限流降级

importjava.util.concurrent.Semaphore;publicclassSemaphoreDemo{// 许可证数量为 3,即同时最多 3 个线程能进临界区privatestaticfinalSemaphoresemaphore=newSemaphore(3);publicstaticvoidmain(String[]args){for(inti=1;i<=5;i++){newThread(newTask("线程-"+i)).start();}}staticclassTaskimplementsRunnable{privatefinalStringname;Task(Stringname){this.name=name;}@Overridepublicvoidrun(){try{semaphore.acquire();// 拿许可证,没有就阻塞System.out.println(name+" 进入临界区,当前可用许可证: "+semaphore.availablePermits());Thread.sleep(2000);// 模拟业务耗时System.out.println(name+" 执行完毕");}catch(InterruptedExceptione){e.printStackTrace();}finally{semaphore.release();// 一定要释放!System.out.println(name+" 释放许可证");}}}}

运行后会看到:前 3 个线程同时进入,后 2 个等前面有人释放后才依次进入。

线程-1 进入临界区,当前可用许可证:2线程-4 进入临界区,当前可用许可证:0线程-3 进入临界区,当前可用许可证:1线程-1 执行完毕 线程-4 执行完毕 线程-3 执行完毕 线程-4 释放许可证 线程-3 释放许可证 线程-1 释放许可证 线程-5 进入临界区,当前可用许可证:1线程-2 进入临界区,当前可用许可证:2线程-5 执行完毕 线程-5 释放许可证 线程-2 执行完毕 线程-2 释放许可证 进程已结束,退出代码为0

🎯 典型应用场景对比

场景许可证数量说明
数据库连接池连接数上限例如设置 10,防止连接耗尽
API 接口限流QPS 上限例如设置 50,保护后端服务
爬虫并发控制目标站承受力避免请求过快被封 IP
互斥锁(Mutex)*1等同于Lock,实现线程独占

⚠️ 注意事项

  1. 死锁风险:如果acquire()后未调用release()(如异常中断),会导致永久阻塞。
  2. 公平性:Java 构造器new Semaphore(3, true)可设置为公平模式(FIFO),但会降低性能。
  3. Asyncio 作用域asyncio.Semaphore必须在async def函数内创建,不能在模块顶层创建(会报 Event Loop 错误)。