net.freeutils.util.Throttler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jelementary Show documentation
Show all versions of jelementary Show documentation
The Java Elementary Utilities package
The newest version!
/*
* Copyright © 2003-2024 Amichai Rothman
*
* This file is part of JElementary - the Java Elementary Utilities package.
*
* JElementary is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* JElementary is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JElementary. If not, see .
*
* For additional info see https://www.freeutils.net/source/jelementary/
*/
package net.freeutils.util;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The {@code Throttler} class provides a throttling facility using average and burst speed limits.
*/
public class Throttler {
protected volatile double interval;
protected volatile int burst;
protected final AtomicInteger bucket;
protected volatile double lastUpdate; // must be double to maintain precision
/**
* Constructs a Throttler with given limit and burst size.
*
* @param limit the speed limit
* @param unit the limit time unit
* @param burst the burst size
*/
public Throttler(int limit, TimeUnit unit, int burst) {
setLimit(limit, unit);
setBurst(burst);
bucket = new AtomicInteger(limit);
lastUpdate = System.currentTimeMillis();
}
/**
* Sets the speed limit.
*
* @param limit the speed limit
* @param unit the limit time unit
*/
public void setLimit(double limit, TimeUnit unit) {
this.interval = unit.toMillis(1) / limit;
}
/**
* Sets the burst size.
*
* @param burst the burst size
*/
public void setBurst(int burst) {
this.burst = burst;
}
/**
* Throttles the calling thread by blocking for as long as the speed limit is exceeded.
*
* @throws InterruptedException if the calling thread is interrupted while blocked
*/
public void throttle() throws InterruptedException {
for (;;) {
int current = bucket.get();
if (current > 0) {
if (bucket.compareAndSet(current, current - 1))
return;
} else {
long delay = updateTokens();
if (delay > 0)
Thread.sleep(delay);
}
}
}
/**
* Updates the current supply of tokens in the bucket.
*
* @return the minimum number of milliseconds to wait until additional tokens may become available,
* or zero if there is at least one token already available
*/
private synchronized long updateTokens() {
if (bucket.get() > 0)
return 0;
double elapsed = System.currentTimeMillis() - lastUpdate;
int intervalsCount = (int)(elapsed / interval);
double intervalsElapsed = intervalsCount * interval;
int tokens = Math.min(burst, intervalsCount);
if (tokens == 0)
return (long)(interval - (elapsed - intervalsElapsed)); // milliseconds until next fully elapsed interval
lastUpdate += intervalsElapsed;
bucket.addAndGet(tokens);
return 0;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy