Semaphore

用来限制同一时间访问某个资源的线程数量

核心方法

  • acquire() 获取一个许可,若无可用则阻塞等待

  • acquire(int permits) 一次获取多个许可

  • tryAcquire() 尝试获取,不阻塞,会返回 true/false

  • tryAcquire(long timeout, TimeUnit unit) 显示等待获取许可

  • release() 释放一个许可

  • availablePermits() 返回当前可用许可

  • drainPermits() 一次性拿走所有许可

示例

复制代码
public class SemaphoreDemo {
​
    private static final Semaphore semaphore = new Semaphore(3); 
    
    public static void main(String[] args) {
        
        for (int i = 0; i < 10; i++) {
            final int finalI = i;
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println("acquire semaphore");
                    System.out.println(Thread.currentThread().getName() + " execute " + finalI);
                    Thread.sleep(2000);
                } catch (Exception e) {
​
                } finally {
                    semaphore.release();
                    System.out.println("task finish and release semaphore");
                }
            }).start();
        }
    }
}

我们可以看到,控制台上每次只有 3 个线程可以执行任务

也可以尝试获取许可

复制代码
if (semaphore.tryAcquire()) {
    try {
        System.out.println("成功获取许可,执行任务");
    } finally {
        semaphore.release();
    }
} else {
    System.out.println("当前无许可可用,放弃执行");
}

也可以选择设置等待时间,而不是立即返回失败

复制代码
if (semaphore.tryAcquire(2, TimeUnit.SECONDS)) {
    try {
        System.out.println("在2秒内拿到了许可");
    } finally {
        semaphore.release();
    }
} else {
    System.out.println("2秒超时仍未拿到许可");
}

Semaphore 支持公平锁和非公平锁

复制代码
Semaphore semaphore = new Semaphore(3, true);

默认 false 非公平,后来的线程可能插队,吞吐量更高;true 公平,按线程申请顺序排队,吞吐量低,但更公平

接下来对比下 Semaphore 和其他限流手段

限流方式 作用范围 精度 是否分布式 优点 缺点
Semaphore 单JVM本地 并发数级 简单高效、轻量 无法跨实例全局限流
Guava RateLimiter 单JVM本地 QPS级 支持速率限制、平滑突发 精度较粗
Sentinel / Redis 分布式 QPS级 可动态调整限流规则 依赖外部组件
Nginx / Gateway API网关层 请求速率 统一控制入口 配置复杂

因此,Semaphore 适合保护内部资源(线程池、数据库连接、文件IO等)

相关推荐
信誓旦旦的程序猿37 分钟前
【量化系统从零构建 #04】存储设计:选型·建库·交易日历
java·人工智能·python·股票数据api·股票数据·股票数据api接口·股票api数据接口
yurenpai(27届找实习中)37 分钟前
Java 10 的 var 为什么不能随便用?从订单汇总看懂类型推断与泛型陷阱
java·开发语言·java18
Flynt2 小时前
把公司项目迁到 Spring Boot 4.0:编译通过只是开始
java·spring boot·后端
Tangyuewei2 小时前
给老 Spring 项目装个 AI Agent
java·人工智能·spring
她的男孩2 小时前
多租户和数据权限怎么共存?扒完拦截器注册链路,我找到 4 个隐蔽的坑
java·后端·架构
小溪学编程3 小时前
Java BufferedReader 详解:从基础用法到性能优化
java·python·性能优化
HZero.chen3 小时前
Java 匿名类简介
java·匿名类
用户3126874877203 小时前
Java 泛型擦除到底擦掉了什么?类型安全与桥接方法深度拆解
java
暖焰核心3 小时前
继承全解——继承、默认成员函数、切片、隐藏与虚继承
java·前端·javascript
君顾13 小时前
外卖CPS系统开发实战指南:从架构设计到部署全流程解析
java·开发语言·外卖