javaadvanced

Rate Limiter — Token Bucket Algorithm

Implement a thread-safe rate limiter using the token bucket algorithm for API throttling.

java
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

public class RateLimiter {
    private final long maxTokens;
    private final long refillRate; // tokens per second
    private long availableTokens;
    private long lastRefillTime;
    private final ReentrantLock lock = new ReentrantLock();

    public RateLimiter(long maxTokens, long refillRate) {
        this.maxTokens = maxTokens;
        this.refillRate = refillRate;
        this.availableTokens = maxTokens;
        this.lastRefillTime = System.nanoTime();
    }

    public boolean tryAcquire() {
        return tryAcquire(1);
    }

    public boolean tryAcquire(int tokens) {
        lock.lock();
        try {
            refill();
            if (availableTokens >= tokens) {
                availableTokens -= tokens;
                return true;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    private void refill() {
        long now = System.nanoTime();
        long elapsed = now - lastRefillTime;
        long tokensToAdd = elapsed * refillRate / 1_000_000_000L;
        if (tokensToAdd > 0) {
            availableTokens = Math.min(maxTokens, availableTokens + tokensToAdd);
            lastRefillTime = now;
        }
    }

    public long getAvailableTokens() {
        lock.lock();
        try {
            refill();
            return availableTokens;
        } finally {
            lock.unlock();
        }
    }

    // Per-user rate limiter
    private static final java.util.concurrent.ConcurrentHashMap<String, RateLimiter> limiters
        = new java.util.concurrent.ConcurrentHashMap<>();

    public static RateLimiter forUser(String userId) {
        return limiters.computeIfAbsent(userId,
            k -> new RateLimiter(10, 2)); // 10 burst, 2/sec refill
    }

    public static void main(String[] args) throws Exception {
        RateLimiter limiter = new RateLimiter(5, 1); // 5 burst, 1/sec

        for (int i = 0; i < 10; i++) {
            boolean allowed = limiter.tryAcquire();
            System.out.printf("Request %d: %s (tokens: %d)%n",
                i, allowed ? "ALLOWED" : "BLOCKED", limiter.getAvailableTokens());
            Thread.sleep(300);
        }
    }
}

Use Cases

  • API rate limiting per user or IP
  • Throttling outbound requests to external services
  • Controlling resource consumption in multi-tenant systems

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.