Why AtomicInteger Outperforms Random() in High‑Concurrency Random Number Generation

The article analyzes two common random‑number scenarios in software testing, identifies a CPU‑heavy custom random method, benchmarks ThreadLocalRandom, AtomicInteger, and plain int implementations under multi‑ and single‑thread loads, and proposes a lightweight AtomicInteger‑based selector that consistently delivers the best performance.

FunTester
FunTester
FunTester
Why AtomicInteger Outperforms Random() in High‑Concurrency Random Number Generation

Problem

During high‑throughput API testing a custom method

com.funtester.frame.SourceCode#random(java.util.List<F>)

became a CPU bottleneck. The test suite could generate >500 k QPS on a single machine, but the repeated calls to the random selector limited overall throughput.

Initial Investigation

Profiling showed that the custom random implementation consumed a disproportionate amount of CPU when invoked twice per request (e.g., selecting a driver and a URL for each simulated user). The goal was to replace it with a faster generator.

Alternative Random Generator

Java’s java.util.concurrent.ThreadLocalRandom is the fastest built‑in source. A small helper was added:

/**
 * Get a random integer between 1 and <em>num</em> (inclusive).
 * @param num upper bound (inclusive)
 * @return random integer
 */
public static int getRandomInt(int num) {
    return ThreadLocalRandom.current().nextInt(num) + 1;
}

Sequential Access Idea

Instead of picking a random element each time, the collection can be traversed sequentially using a rotating index. The idea was demonstrated with a traffic‑replay scenario that simulates 100 000 users:

def funtest = {
    random(drivers).getGetResponse(random(urls))
}
new FunQpsConcurrent(funtest).start()

Because the test calls random twice per request, the method’s overhead becomes noticeable when QPS reaches the 100 k range.

Multi‑Threaded Benchmark

Three variants were benchmarked under identical load (10 000 selections per thread, 500 threads, 1 000 iterations):

Original random method.

Rotating index backed by java.util.concurrent.atomic.AtomicInteger.

Rotating index backed by a plain int variable.

package com.funtest.groovytest;

import com.funtester.base.constaint.FixedThread;
import com.funtester.frame.SourceCode;
import com.funtester.frame.execute.Concurrent;
import java.util.concurrent.atomic.AtomicInteger;

class FunTest extends SourceCode {
    static int times = 1000;
    static int thread = 500;
    static def integers = 0..100 as List;
    static def integer = new AtomicInteger();
    static def i = 0;
    static def size = integers.size();

    public static void main(String[] args) {
        RUNUP_TIME = 0;
        new Concurrent(new FunTester(), thread, "Test random performance").start();
    }

    private static class FunTester extends FixedThread {
        FunTester() { super(null, times, true); }
        @Override protected void doing() throws Exception {
            10000.times { random(integers) }               // original
            // 10000.times { integers.get(integer.getAndIncrement() % size) } // AtomicInteger
            // 10000.times { integers.get(i++ % size) }                     // int
        }
        @Override FunTester clone() { return new FunTester(); }
    }
}

All variants quickly saturated the CPU, so a single run per variant was recorded:

random: 1151

AtomicInteger: 3152

int: 2273

Surprisingly, the AtomicInteger version achieved the highest throughput, suggesting that the atomic increment plus modulo operation incurs less overhead than the original method’s internal randomness.

Single‑Threaded Benchmark

A comparable test was executed in a single‑threaded context (1 000 000 selections):

package com.funtest.groovytest;

import com.funtester.frame.SourceCode;
import java.util.concurrent.atomic.AtomicInteger;

class FunTestT extends SourceCode {
    static int times = 1000000;
    static def integers = 0..100 as List;
    static def integer = new AtomicInteger();
    static def i = 0;
    static def size = integers.size();

    public static void main(String[] args) {
        time {
            // times.times { random(integers) }
            // times.times { integers.get(integer.getAndIncrement() % size) }
            times.times { integers.get(i++ % size) }
        }, "Random performance test";
    }
}

Execution times (milliseconds) were:

random: 763 ms

AtomicInteger: 207 ms

int: 270 ms

The AtomicInteger selector was consistently fastest in both multi‑ and single‑threaded scenarios.

Final Implementation

The preferred approach was encapsulated in a reusable method that validates the list and uses a monotonically increasing AtomicInteger index to select elements:

/**
 * Randomly select an object from a list using a rotating AtomicInteger index.
 * @param list the source list
 * @param index an AtomicInteger that provides a monotonically increasing index
 * @return the selected element
 */
public static <F> F random(List<F> list, AtomicInteger index) {
    if (list == null || list.isEmpty())
        ParamException.fail("Array cannot be empty!");
    return list.get(index.getAndIncrement() % list.size());
}

Replacing the original random method with this lightweight selector eliminated the CPU bottleneck observed in high‑throughput interface testing.

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.

JavaconcurrencyPerformance TestingThreadLocalRandomRandom Number GenerationAtomicInteger
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.