JMeter Thread Groups Explained: Users, Ramp-Up, and Loops
How JMeter Thread Groups control virtual users, ramp-up time, and loop count, and how to choose values that actually model your real traffic pattern.
The Thread Group is where you define how much load a JMeter test generates, and it’s also where the most common modeling mistakes happen — because its three basic settings (threads, ramp-up, loops) are easy to set without thinking about what traffic pattern they actually produce.
The three core settings
- Number of Threads (users) — how many virtual users run concurrently.
- Ramp-Up Period — how many seconds JMeter takes to start all threads. A 100-user group with a 100-second ramp-up starts 1 user/second.
- Loop Count — how many times each thread repeats its set of samplers. “Forever” runs until the test is manually stopped or a duration limit is hit.
Standard Thread Group vs other types
The default Thread Group is enough for most fixed-concurrency tests. JMeter also offers:
- stepping thread group (plugin) — increases load in steps, useful for finding the breaking point.
- Ultimate Thread Group (plugin) — lets you define multiple load phases (ramp-up, hold, ramp-down) in one chart-like configuration.
- Arrivals Thread Group (plugin, newer JMeter Plugins) — models arrival rate (requests/sec) directly instead of concurrent users, closer to how k6 and Gatling think about load.
Concurrency vs arrival rate: pick the right model
A plain Thread Group with fixed threads models closed-system behavior: a fixed pool of users, each waiting for a response before sending the next request. This is right for internal/enterprise apps with a known user base. It is wrong for modeling public web traffic, where new users arrive at some rate regardless of how fast the system responds (an open system). For open-system modeling, use an Arrival Thread Group or a Constant Throughput Timer tuned to a target rate, not raw thread count, as your primary lever.
Choosing ramp-up
A ramp-up too short (e.g. 1,000 users in 5 seconds) creates an artificial spike that may trigger false positives (connection pool exhaustion, autoscaling lag) unrelated to steady-state capacity. A ramp-up too long delays reaching your target load and wastes test time. A reasonable default: ramp-up so that each new user starts roughly 1 second apart up to a few hundred users, then accept proportionally faster ramps for larger counts, validated against what you’re actually trying to learn (steady-state capacity vs spike resilience are different tests with different ramp-up needs).
Loop count and test duration
For steady-state tests, prefer Loop Count = Forever combined with a Duration field (under the Thread Group’s scheduler) over a fixed loop count — duration-based tests give you a controllable, comparable run length regardless of how response times vary, while a fixed loop count makes total test time unpredictable as the system slows down.
Takeaway: before tuning thread count, decide whether you’re modeling a closed system (fixed user pool) or an open system (arrival rate) — using the wrong one will give you numbers that don’t predict production behavior, no matter how carefully you tune ramp-up.
Comments are powered by Giscus (GitHub Discussions). Enable them by
configuring GISCUS in src/consts.ts — see
giscus.app.