本文目录导读:

截至2025年5月,Java标准库(JDK)中没有内置现成的令牌桶(Token Bucket)算法API,但可以通过以下几种方式实现:
使用第三方库(推荐)
Google Guava(最常用)
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.2.0-jre</version>
</dependency>
// 创建令牌桶 - 每秒2个令牌 RateLimiter limiter = RateLimiter.create(2.0); // 每秒产生2个令牌 // 尝试获取令牌(阻塞) double waitTime = limiter.acquire(); // 获取1个令牌,等待直到获取成功 // 尝试获取令牌(非阻塞) boolean acquired = limiter.tryAcquire(); // 立即返回 boolean acquired = limiter.tryAcquire(5, TimeUnit.SECONDS); // 最多等待5秒
Guava RateLimiter 特点
- 基于令牌桶算法(实际上是漏斗算法的变体)
- 支持平滑突发(SmoothBursty)和预热(SmoothWarmingUp)
- 支持预消费
自己实现(简单示例)
public class TokenBucket {
private final long capacity; // 桶容量
private final double refillRate; // 每秒生成的令牌数
private double tokens; // 当前令牌数
private long lastRefillTimestamp; // 上次刷新时间
public TokenBucket(long capacity, double refillRate) {
this.capacity = capacity;
this.refillRate = refillRate;
this.tokens = capacity;
this.lastRefillTimestamp = System.nanoTime();
}
public synchronized boolean tryConsume(int tokens) {
refill();
if (this.tokens >= tokens) {
this.tokens -= tokens;
return true;
}
return false;
}
private void refill() {
long now = System.nanoTime();
double tokensToAdd = (now - lastRefillTimestamp) * refillRate / 1_000_000_000.0;
this.tokens = Math.min(capacity, this.tokens + tokensToAdd);
this.lastRefillTimestamp = now;
}
}
// 使用
TokenBucket bucket = new TokenBucket(10, 2); // 容量10,每秒2个令牌
boolean success = bucket.tryConsume(1); // 尝试获取1个令牌
使用 Java 并发工具 + 定时器
public class TokenBucketScheduled {
private final BlockingQueue<Object> bucket;
private final Object token = new Object();
public TokenBucketScheduled(int capacity, int rate) {
bucket = new LinkedBlockingQueue<>(capacity);
// 初始化装满令牌
for (int i = 0; i < capacity; i++) {
bucket.offer(token);
}
// 定时向桶中添加令牌
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
bucket.offer(token); // 桶满时丢弃
}, 0, 1000 / rate, TimeUnit.MILLISECONDS);
}
public boolean tryConsume() {
return bucket.poll() != null; // 非阻塞
}
public void consume() throws InterruptedException {
bucket.take(); // 阻塞等待
}
}
企业级方案
Resilience4j(支持令牌桶)
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
<version>2.2.0</version>
</dependency>
RateLimiterConfig config = RateLimiterConfig.custom()
.limitForPeriod(10) // 周期内允许的请求数
.limitRefreshPeriod(Duration.ofSeconds(1)) // 刷新周期
.timeoutDuration(Duration.ofMillis(500)) // 等待超时
.build();
RateLimiter rateLimiter = RateLimiter.of("myService", config);
// 装饰你的函数
Supplier<String> decorated = RateLimiter.decorateSupplier(rateLimiter, () -> "result");
| 方案 | 推荐场景 | 是否内置 |
|---|---|---|
| Guava RateLimiter | 通用场景 ✅ 首选 | 第3方库 |
| Resilience4j | 微服务/异步场景 | 第3方库 |
| 自己实现 | 简单场景/学习 | 手动 |
| JDK并发工具 | 基础控制 | 组合实现 |
Java 标准库没有直接提供令牌桶 API,但 Guava 的 RateLimiter 是最常用、最成熟的替代方案。