Chapter 04Lesson 01~120 minutes

Thread Groups, Virtual Users, Ramp-Up, Loops, and Workload Modeling: Core Concepts and Mental Model

Thread Group fields are not arbitrary “load knobs.” They describe how virtual users are created and allowed to repeat a scenario. The target request rate that actually occurs is an outcome of that schedule, the scenario, timers, response time, errors, and generator/target limits.

Thread lifecycleRamp-upLoopsClosed modelArrival model

Learning objectives

  • Separate configured threads, active threads, iteration rate, request rate, arrival rate, and achieved throughput.
  • Trace one classic Thread Group user from thread start through repeated scenario loops and termination.
  • Explain exact ramp-up behavior and why a thread count is not an RPS target.
  • Distinguish loop-bound and duration-bound termination, including in-flight sampler behavior.
  • Contrast closed/concurrency-oriented and open/arrival-oriented workload questions.
  • Identify generator and target evidence required before interpreting queueing or capacity.

1. From tree semantics to workload semantics

Chapter 03 answered “what does one thread execute?” Chapter 04 asks “how many scenario executions exist over time, when do they start, how often do they repeat, and what rate does that produce?” This is the point where an otherwise correct JMX can become an invalid performance experiment.

Safety boundary: all executable examples remain on http://127.0.0.1:8000. Chapter labs use at most 4 classic threads, short durations, deliberate timers, and small sample counts. Do not translate these examples into higher traffic or external targets without a new authorization/capacity envelope.

2. Mental model: workload objective to achieved rate

Configured workload becomes achieved workload through feedback

Use this diagram as a workload-state model, not as a promise that configured values become achieved values. The prose below explains where feedback from response time, timers, generator limits, and target limits enters.

flowchart TD
Q[Performance question] --> M[Workload model]
M --> TG[Thread Group / schedule]
TG --> A[Thread creation or scenario arrivals]
A --> L[Per-thread scenario loop]
L --> T[Think time / pacing timers]
T --> S[Sampler(s)]
S --> R[Response time + correctness]
R --> L
S --> O[Observed throughput / active threads / errors]
G[Generator CPU / heap / sockets / network] --> O
U[Target CPU / queues / pools / downstreams] --> O
O --> V[Validity judgment]

Classic GUI vocabulary: the field named Number of Threads (users) is the configured classic-user count. It is not a request-rate field.

A workload objective is the demand pattern the experiment is trying to represent. The Thread Group turns that intent into thread creation, ramp-up, loops, or a bounded lifetime. Each thread executes the JMX scenario. Timers add think time/pacing before samplers. Sampler response time then feeds back into how quickly a closed user can begin the next iteration. Generator and target limits can slow or prevent the intended schedule from being achieved.

3. Vocabulary: user lifecycle, arrivals, and rates

Term Precise meaning here Do not substitute
Configured threads Maximum user threads requested by a classic Thread Group. Requests/second.
Active threads Threads currently alive at a particular sample observation. Configured threads; ramp-up/termination make them differ.
Iteration One traversal of the Thread Group's test case/loop body. One request when the scenario has multiple samplers.
Iteration rate Completed scenario iterations per time unit. Request rate.
Request/sample rate Sampler executions/results per time unit. User arrivals.
Arrival rate Rate at which new independent scenarios/users are started in an open model. Classic active-user count.
Ramp-up Classic Thread Group interval over which configured threads are started. Warm-up or request-rate ramp by itself.
Think time Intentional user wait, usually modeled with a Timer. Server latency.
Duration Thread Group lifetime bound when scheduler/lifetime is enabled. A guarantee that an in-flight sampler ends exactly at that second.
Achieved throughput Observed completed samples/transactions per time unit. Configured demand.

4. Classic Thread Group is naturally concurrency-oriented

In a closed user model, a fixed or ramped pool of threads repeatedly runs the scenario. A thread cannot begin its next iteration while it is still waiting for the current sampler and think time. If the target slows down, the same users complete work less often and achieved throughput may fall. That feedback is a defining property of a closed workload.

This is often appropriate for bounded populations such as support agents or logged-in interactive users. It is not automatically appropriate for independent external events that keep arriving even while earlier work is slow.

5. Ramp-up controls thread starts, not a request-rate slope

The current classic Thread Group documentation defines Ramp-up Period as how long JMeter should take to get all threads started. With 10 threads and 100 seconds, starts are spaced about 10 seconds apart. The first thread starts immediately, so the tenth starts around 90 seconds rather than at second 100.

A fast ramp can intentionally create a startup burst, but it can also accidentally spike the target or the injector. A very long ramp can mean the early part of the test never reaches the intended concurrent population. Choose ramp from the business scenario and experiment design, not from a generic “one second per user” folklore rule.

6. Loop Count controls scenario repetition

Loop Count says how many times each classic thread performs its test case. If one thread has five loops and the scenario has one sampler, the maximum configured sample count is five. If the same scenario contains three samplers, the maximum becomes fifteen samples—assuming no controller logic, failures, duration bounds, or early stop changes the path.

“Infinite” is a valid Thread Group option, but it is operationally dangerous without an independent lifetime bound. A bounded course or CI profile should make its stop condition explicit before starting.

7. Scheduler duration and loops race: whichever ends first

When Thread Group lifetime/scheduler is enabled, JMeter runs until the loop condition finishes or the duration/end-time boundary is reached, whichever comes first. The boundary is checked between samples. An HTTP sampler already waiting for a response is not automatically interrupted merely because the nominal duration has elapsed.

Consequence: “duration = 10 seconds” means the group is bounded around that window, not that the process must finish at exactly 10.000 seconds. Slow in-flight samples can make the observed end later. Preserve timeouts and stop/abort procedures separately.

8. Open arrival models answer a different question

An open model schedules new scenario arrivals independently of how long earlier scenarios take. If the target slows, active concurrency can grow because new work continues arriving. This is useful for demands such as incoming messages, externally scheduled API jobs, or traffic that does not politely wait for previous users to finish.

JMeter 5.6.3 includes an Open Model Thread Group, but the current Component Reference explicitly marks it experimental and warns that it may change. Its schedule syntax uses constructs such as rate(1/sec) random_arrivals(30 sec) rate(2/sec). Treat those as scenario/user arrivals; when a scenario has multiple samplers, arrival rate is not identical to raw request rate.

9. Stable rate-oriented fallback: throttle departures, measure achievement

The built-in Precise Throughput Timer can create a rate-oriented departure schedule for samples. It does not create threads, so enough classic threads must exist to serve the schedule. If the target slows, timers are too large, or the generator lacks capacity, achieved throughput can be lower than the configured target.

This makes it useful as a stable teaching fallback for “try to issue this many samples over this period,” while preserving an important limitation: it is not the same semantic model as independent open user arrivals.

10. Queueing symptoms require both sides of the experiment

When offered work exceeds a target's ability to complete it, response time, active concurrency, queue depth, errors, or timeouts may rise. But similar symptoms can come from the injector: CPU saturation, GC, sockets, DNS, result writing, or heavy listeners.

Evidence What it tells you What it cannot prove alone
JTL grpThreads/allThreads Thread counts observed when samples are recorded. Continuous thread history between samples.
JTL throughput/timestamps Achieved sample completions over time. Configured demand was fully achieved.
Fixture/server active/max-active Observed server-side concurrency. Production queue behavior in another system.
Generator CPU/heap/network Whether injector headroom looks healthy. Root cause in the SUT.
SUT queue/pool/CPU telemetry Server-side saturation clues. Whether the generator issued the intended workload.

11. Read-only workload inspection before changing Thread Group fields

  • Record Thread Group type, thread count, ramp-up, loop count, scheduler/lifetime, and startup delay.
  • Count samplers/transactions in one scenario iteration and note controller branches.
  • List timers and their scopes; separate user think time from target response time.
  • Inspect existing JTL headers. JMeter's current property reference says thread counts are saved by default.
  • Record generator and target version/environment identity before comparison.
  • Predict configured maximum samples and likely active-user shape before running.

12. Why this matters in DevOps

Preserve the JTL and jmeter.log beside the workload manifest so a reviewer can distinguish sample behavior from engine/runtime diagnostics.

A CI gate that says “500 threads passed” is meaningless unless those threads represent an operational demand model and the achieved workload is measured. Release evidence should name the workload class, schedule, target build/environment, timers, achieved throughput, errors, percentiles, generator headroom, and validity window.

Knowledge check

Why does 20 classic JMeter threads not mean 20 requests per second?

With 10 threads and a 100-second ramp-up, when does the tenth thread approximately start?

Why can a duration-bounded Thread Group finish after its nominal duration?

What changes when a target slows under a closed user model?

Why is Open Model Thread Group not the mandatory course baseline?

Next lesson

Change one workload factor at a time

Lesson 2 runs conservative loopback experiments that vary threads, ramp-up, loops, duration, and timers separately, then uses JTL thread counts, throughput, percentiles, fixture concurrency, and generator monitoring to explain the result.

Official references and version notes

  • Component Reference — current classic Thread Group, Open Model Thread Group, Precise Throughput Timer, and timer semantics.
  • Elements of a Test Plan — sampler ordering, Thread Groups, timer purpose/scope, and listener guidance.
  • Properties Reference — CSV result-save settings including thread-count fields and timestamp behavior.
  • Getting Started — GUI authoring versus CLI load execution and command-line behavior.
  • Best Practices — generator/listener practices for load execution.
  • Apache JMeter downloads — current production release and release-specific Java requirement.
Version and compatibility note

Version-sensitive statements were rechecked against current Apache JMeter primary documentation on 2026-09-04. The production baseline remains Apache JMeter 5.6.3; the release requires Java 8+, while mandatory labs use a Java 17 JDK. The classic Thread Group controls user/thread count, ramp-up, loops, and optional lifetime; with its scheduler, JMeter stops when loops finish or duration/end-time is reached, whichever occurs first, but the check happens between samples and an in-flight sampler waiting for a response is not forcibly ended by that scheduler boundary. The current Open Model Thread Group is explicitly marked experimental and may change; its schedule is evaluated at test start and its schedule end terminates/intercepts active scenario threads unless a tail pause(...) leaves time to finish. The stable mandatory rate-oriented path in this chapter uses the classic Thread Group plus the built-in Precise Throughput Timer. That timer does not create threads and cannot guarantee achieved throughput when too few threads, generator limits, target limits, or other delays prevent the schedule from being served.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.