线程为何需获取信号量的多个许可?求acquire(int permits)示例
为什么要调用Semaphore的acquire(int permits)?
当调用acquire()获取1个许可就能访问共享资源时,acquire(int permits)的存在是为了应对需要批量占用资源配额的场景,核心价值在于:
- 匹配资源的实际消耗粒度:有些共享资源不是以“单次访问”为最小单位,比如一个线程需要同时使用3个数据库连接、或者占用内存池的5个块,这时候一次性申请对应数量的许可,能精准匹配资源的实际使用量,避免资源被零散占用导致的浪费。
- 原子性的配额申请:批量申请是原子操作,不会出现“申请了部分许可后被阻塞”的情况,从根源上避免了因资源碎片化引发的死锁或调度混乱。
- 精细化的容量控制:比如系统总资源配额是固定值(如总带宽10单位),不同任务的资源消耗不同(大任务占3单位,小任务占1单位),通过
acquire(int permits)可以灵活控制任务并发数,保证总资源不超限。
使用示例:基于带宽配额的下载任务控制
假设服务器总带宽配额为10,大文件下载需要占用3个带宽单位,小文件占用1个。用Semaphore来实现带宽的精细化管控:
import java.util.concurrent.Semaphore; public class BandwidthController { // 总带宽配额为10 private static final Semaphore bandwidthSemaphore = new Semaphore(10); public static void main(String[] args) { // 启动3个大文件下载任务 for (int i = 0; i < 3; i++) { new Thread(() -> downloadLargeFile()).start(); } // 启动4个小文件下载任务 for (int i = 0; i < 4; i++) { new Thread(() -> downloadSmallFile()).start(); } } private static void downloadLargeFile() { try { // 申请3个带宽配额 bandwidthSemaphore.acquire(3); System.out.println(Thread.currentThread().getName() + " 开始大文件下载,占用3个带宽配额"); // 模拟下载耗时 Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 释放3个带宽配额 bandwidthSemaphore.release(3); System.out.println(Thread.currentThread().getName() + " 完成大文件下载,释放3个带宽配额"); } } private static void downloadSmallFile() { try { // 申请1个带宽配额 bandwidthSemaphore.acquire(1); System.out.println(Thread.currentThread().getName() + " 开始小文件下载,占用1个带宽配额"); // 模拟下载耗时 Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 释放1个带宽配额 bandwidthSemaphore.release(1); System.out.println(Thread.currentThread().getName() + " 完成小文件下载,释放1个带宽配额"); } } }
运行这段代码可以看到:3个大任务(各占3配额)会先占用9个配额,剩下1个配额只能启动1个小任务;当某个大任务完成释放3配额后,剩余的小任务才能陆续启动,始终保证总带宽配额不超过10。
内容的提问来源于stack exchange,提问作者taher bouali
相关产品推荐
相关产品推荐

