Це залежить від програми.
Уявіть собі випадок, коли кілька потоків хочуть, щоб маркер здійснив глобальну дію з обмеженою швидкістю без дозволу (тобто ви хочете обмежити 10 дій на 10 секунд, але ви не хочете, щоб 10 дій відбувалися в першій секунді, а потім залишалися 9 секунд зупинився).
У DelayedQueue є недолік: порядок, в якому потоки запитують жетони, може бути не тим порядком, коли вони отримують свій запит. Якщо кілька потоків заблоковано в очікуванні маркера, незрозуміло, який з них буде приймати наступний доступний маркер. На мою точку зору, ви навіть можете чекати теми, які вічно чекають.
Одне рішення - мати мінімальний проміжок часу між двома послідовними діями та здійснювати дії в тому ж порядку, як вони просили.
Ось реалізація:
public class LeakyBucket {
protected float maxRate;
protected long minTime;
//holds time of last action (past or future!)
protected long lastSchedAction = System.currentTimeMillis();
public LeakyBucket(float maxRate) throws Exception {
if(maxRate <= 0.0f) {
throw new Exception("Invalid rate");
}
this.maxRate = maxRate;
this.minTime = (long)(1000.0f / maxRate);
}
public void consume() throws InterruptedException {
long curTime = System.currentTimeMillis();
long timeLeft;
//calculate when can we do the action
synchronized(this) {
timeLeft = lastSchedAction + minTime - curTime;
if(timeLeft > 0) {
lastSchedAction += minTime;
}
else {
lastSchedAction = curTime;
}
}
//If needed, wait for our time
if(timeLeft <= 0) {
return;
}
else {
Thread.sleep(timeLeft);
}
}
}