案例
public static void main(String[] args) {
// 创建一个RateLimiter,速率为每秒1个许可
RateLimiter rateLimiter = RateLimiter.create(1.0);
// 预先消耗所有的初始许可
rateLimiter.acquire(1);
// 创建并启动多个线程来模拟高并发
for (int i = 0; i < 5; i++) {
final int threadNumber = i;
new Thread(() -> {
long start = System.currentTimeMillis();
boolean acquired = rateLimiter.tryAcquire(1, 1000, TimeUnit.MILLISECONDS);
long end = System.currentTimeMillis();
long diff = end - start;
if (acquired) {
System.out.println("Thread " + threadNumber + " acquired permit: " + diff + " ms");
} else {
System.out.println("Thread " + threadNumber + " could not acquire permit (timeout): " + diff + " ms");
}
}).start();
}
}
输出结果
Thread 4 could not acquire permit (timeout): 0 ms
Thread 1 could not acquire permit (timeout): 0 ms
Thread 2 could not acquire permit (timeout): 0 ms
Thread 3 could not acquire permit (timeout): 0 ms
Thread 0 acquired permit: 999 ms
解释
官方注解
Acquires the given number of permits from this RateLimiter if it can be obtained without exceeding the specified timeout, or returns false immediately (without waiting) if the permits would not have been granted before the timeout expired.
获取指定数量的许可,如果可以在不超过指定超时时间的情况下获得许可,否则立即返回false
(不等待),如果许可在超时之前不能被授予。
在单线程示例中,每次调用大约等待一秒钟就能获得许可,因此不会抛出任何异常。这是你的代码中发生的情况:
- 第一次调用知道可以立即获得许可,所以它立即获得许可。
- 在第一次调用完成后,第二次调用知道如果等待大约 1 秒钟可以获得许可,所以它等待大约 1 秒并获得许可。
- 在第二次调用完成后,第三次调用知道如果等待大约 1 秒钟可以获得许可,所以它等待大约 1 秒并获得许可。
- 在第三次调用完成后,第四次调用知道如果等待大约 1 秒钟可以获得许可,所以它等待大约 1 秒并获得许可。
- 程序结束。
现在,尝试在多线程示例中使用这个,你将开始看到一些失败和一些成功。因为它们都想同时获得许可。
- 第一个获得许可的线程很高兴。
- 然后第二个线程知道如果等待大约 1 秒钟,它可以获得许可,所以它等待直到获得许可。
- 第三个和第四个线程看到队列中已经有两个调用,知道它们必须等待 2 秒钟才能获得许可。所以它们放弃了,因为你设置的 1 秒超时已经超过了 2 秒。
在这个多线程示例中,你会看到多个线程同时尝试获取许可时,会出现一些线程成功获取许可,而其他线程因超时而失败。
总结
- tryAcquire的超时参数,是在预期超时之前能够获取到令牌才会阻塞等待,否则会理解返回获取失败