Controlling Java Async QPS with Semaphore and ThreadPool

This article explains how to use Java's java.util.concurrent.Semaphore together with a fixed-size thread pool to enforce a precise QPS limit for asynchronous tasks, providing API details, implementation steps, and a complete test example.

FunTester
FunTester
FunTester
Controlling Java Async QPS with Semaphore and ThreadPool

API Overview

Constructor: java.util.concurrent.Semaphore#Semaphore(int) – the integer sets the total number of permits.

Acquire permit: java.util.concurrent.Semaphore#acquire() – there are overloaded versions with timeout parameters.

Release permit: java.util.concurrent.Semaphore#release() – releases one permit by default.

Both acquire and release support multiple permits.

Implementation

The solution keeps the existing asynchronous thread‑pool mechanism and adds a semaphore to limit the QPS. Each task first acquires a permit, runs, sleeps for one second, then releases the permit.

Because the helper class com.funtester.frame.SourceCode is a Java utility, the implementation stays in Java and uses a Groovy‑to‑Java closure conversion to maintain compatibility.

/**
 * Fixed QPS asynchronous execution, default 16, adjust via SourceCode#setMaxQps(int)
 * @param f
 */
public static void funner(Closure f) {
    if (!ThreadPoolUtil.acquire()) return;
    fun(JToG.toClosure(() -> {
        sleep(1.0);
        fun(JToG.toClosure(() -> {
            f.call();
            ThreadPoolUtil.release();
            return null;
        }));
        return null;
    }));
}

/**
 * Set the maximum QPS for asynchronous tasks
 * @param i
 */
public static void setMaxQps(int i) {
    ThreadPoolUtil.setSemaphore(new Semaphore(i));
}

Global flow‑control variables:

/**
 * Global flow control
 */
static Semaphore semaphore = new Semaphore(16);
static long AcquireTimeout = 8888;
/**
 * Acquire a permit
 */
static boolean acquire() {
    return semaphore.tryAcquire(AcquireTimeout, TimeUnit.MILLISECONDS);
}
/**
 * Release a permit
 */
static def release() {
    semaphore.release();
}

Test Case

The test sets a low QPS (2) and repeatedly submits asynchronous tasks that print the current time.

static void main(String[] args) {
    setMaxQps(2);
    10.times {
        funner {
            output(Time.getDate());
        }
    }
}

Sample console output shows tasks being throttled according to the QPS limit. A noted limitation is that when the thread pool becomes full, tasks may pile up; the article leaves this issue for future handling.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaconcurrencyThreadPoolsemaphoreQPS
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.