Backend Development 4 min read

Using Fixed Thread Pools with CountDownLatch and Phaser for High‑Volume API Requests in Java

This article explains how to replace slow single‑threaded API calls with a fixed‑size Java thread pool combined with CountDownLatch or Phaser for synchronization, achieving a ten‑to‑twenty‑fold performance boost while controlling request pressure.

FunTester
FunTester
FunTester
Using Fixed Thread Pools with CountDownLatch and Phaser for High‑Volume API Requests in Java

In many testing scenarios we need to batch‑request APIs, such as resetting thousands of test accounts or generating test data, and the previous single‑threaded serial execution was too slow for large data volumes.

Two approaches were considered: (1) using Java NIO for asynchronous HTTP requests, and (2) using a thread pool to execute requests concurrently.

The second approach was chosen because the project already uses a synchronous HTTP client (making async client migration costly) and because the first approach makes it difficult to control the request rate, which could cause a sudden spike in service load.

Idea : Use a fixed‑size thread pool and wrap each task as a java.lang.Runnable implementation.

Control pressure by setting the thread pool size.

Synchronize threads using java.util.concurrent.CountDownLatch or java.util.concurrent.Phaser , then shut down the pool.

Implementation : Below are examples of using CountDownLatch and Phaser for synchronization.

java.util.concurrent.CountDownLatch

class MocoReset extends OBase {

    public static void main(String[] args) {
        def keys = DataUtils.getMocoTokens();
        ExecutorService pool = ThreadPoolUtil.createFixedPool(10); // create fixed thread pool
        CountDownLatch latch = new CountDownLatch(keys.size()); // create sync object
        keys.each {
            def base = getBase(it)
            def balance = new Balance(base)
            pool.execute(() -> {
                balance.reset()
                latch.countDown()
            })
        }
        latch.await()
        pool.shutdown()
    }

}

java.util.concurrent.Phaser

class MocoReset extends Base {

    public static void main(String[] args) {
        def keys = DataUtils.getMocoTokens();
        ExecutorService pool = ThreadPoolUtil.createFixedPool(10); // create fixed thread pool
        def phaser = new Phaser(1)
        keys.each {
            def base = getBase(it)
            def balance = new Balance(base)
            pool.execute(() -> {
                phaser.register()
                balance.reset()
                phaser.arriveAndDeregister()
            })
        }
        phaser.arriveAndAwaitAdvance()
        pool.shutdown()
    }

}

Conclusion : Multithreaded programming is very effective in testing; keeping the thread count between 10‑20 is safe, and the theoretical efficiency gain is about 10‑20 times.

JavaConcurrencythreadpoolAPI testingCountDownLatchPhaser
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login 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.