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.

· By perf-test.com Editorial · AI-assisted
jmeterthread-groupsload-testing

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.

Discussions coming soon.

Comments are powered by Giscus (GitHub Discussions). Enable them by configuring GISCUS in src/consts.ts — see giscus.app.