我正在尝试实现令牌桶速率限制器。下面是我编写的基本代码,但没有得到预期的结果。似乎我遗漏了一些东西,或者我还没有完全理解如何在java中线程函数。有人能帮助我寻找正确的方向吗?
public class RateLimiterTokenBucket implements RateLimiter, Runnable{
Integer capacity;
Integer frequencyInMilliseconds;
Integer currentCapacity;
public RateLimiterTokenBucket(Integer frequencyInMilliseconds,Integer capacity) {
this.frequencyInMilliseconds = frequencyInMilliseconds;
this.capacity = capacity;
this.currentCapacity = capacity;
}
@Override
public boolean consume() {
synchronized (currentCapacity) {
if(currentCapacity <= 0) return false;
--currentCapacity;
return true;
}
}
@Override
public void run() {
synchronized (currentCapacity) {
OffsetDateTime t = OffsetDateTime.now();
while(true) {
if(OffsetDateTime.now().isAfter(t.plus(frequencyInMilliseconds, ChronoUnit.MILLIS))){
if(currentCapacity<capacity){
System.out.println("token added");
++currentCapacity;
}
t=OffsetDateTime.now();
}
}
}
}
}
主类
public static void main(String[] args) {
RateLimiter rl = new RateLimiterTokenBucket(10,5);
Thread t = new Thread((Runnable) rl);
t.start();
while(true){
if(rl.consume()) {
System.out.println(OffsetDateTime.now()+" ---> true");
}
}
}
只是得到
2023-04-29T19:30:54.685553+05:30 ---> true
token added
作为两个线程main和t1仍在运行的输出
您的run
方法的整个主体是一个同步的
块。这实际上总是一个错误。在线程完全完成并且run
方法返回之前,您不希望任何其他线程能够执行什么操作?
此外,同步(当前容量)
看起来是一个错误,因为您的程序将不同的对象分配给当前容量
。当一个线程在Integer(5)
上同步而另一个线程在Integer(4)
上同步时意味着什么?如果您从不编写同步(foo)
,您就可以避免犯这个错误,除非foo
是一个最终
变量。
另外值得注意的是:您的主线程在等待下一个令牌时旋转(即,它CPU时间燃烧)。这并不总是一件坏事,但如果您正在运行的主机没有为该线程独占使用保留的CPU,并且有其他线程已准备好运行并等待轮到它们使用CPU,则通常是一件坏事。
P. S.,您是否考虑过使用Semaphore
作为“桶”?您的速率限制器可以调用semaphore.有多少令牌在桶中,并调用
…release()
向桶中添加另一个令牌。并且,您的主线程可以调用semaphore.获取()
来等待下一个令牌。