The Thread Group is the starting point of every JMeter test plan. It answers the fundamental load testing question: how many users, arriving how fast, doing how much? Getting this configuration right is the difference between a test that reflects real usage and one that produces numbers that mean nothing.
What a JMeter thread actually is
In JMeter, a virtual user is a Java OS thread. This is the most important architectural difference from K6, where a virtual user is a lightweight goroutine. A Java thread consumes roughly 1MB of stack space by default. With 500 threads, you are using ~500MB of heap just for thread stacks — before any request data, listener buffers, or JVM overhead.
This is not a limitation to work around; it is a constraint to design for. A single JMeter machine comfortably handles 200–500 threads with a tuned heap. For tests that need thousands of virtual users, distributed testing (covered in Chapter 6) spreads the load across multiple machines.
In K6 terms: 500 JMeter threads ≈ 500 K6 VUs. The workload each simulates is the same — one simulated user looping through the test scenario. The implementation is what differs.
Thread Group core fields
When you add a Thread Group and click it, the editor shows these fields:
Number of Threads (Users) — how many concurrent virtual users. Each thread runs the test plan hierarchy independently, with its own HTTP connection pool and cookie jar.
Ramp-Up Period (seconds) — the time JMeter takes to start all threads. With 100 threads and a 60-second ramp-up, JMeter adds approximately 1.67 threads per second, starting the last thread at t=60s. Setting ramp-up to 0 starts all threads simultaneously — the "thundering herd" that is fine for stress testing but unrealistic for load testing.
Loop Count — how many times each thread repeats the sampler tree. 1 means each thread runs once then exits. 5 means each thread runs five complete iterations. Check the Infinite box to run until you stop the test manually or a duration limit expires.
Same user on each iteration — when checked, cookies and cache persist across iterations for the same thread, simulating a returning user. When unchecked, each iteration starts fresh, simulating a new user session.
Delay Thread creation until needed — defers thread creation to ramp-up time rather than at test start. Useful for very large thread counts where pre-creating all threads strains the JVM before the test begins.
Specify Thread lifetime — reveals a Duration field (run for N seconds instead of N loops) and a Startup delay field (wait before this Thread Group starts). This is how you run multiple Thread Groups on independent schedules within one test plan.
Calculating what your configuration produces
The numbers are straightforward but worth making explicit:
Total iterations = Number of Threads × Loop Count
With 100 threads and 10 loops, each iteration taking roughly 5 seconds:
- Total iterations: 1,000
- Peak concurrent threads: 100 (after ramp-up completes)
- Approximate test duration: ~110 seconds (60s ramp-up + 50s for threads to complete 10 loops)
The actual duration varies based on response times and think time. Sampler response times and timer delays both contribute to iteration duration.
Load patterns with the standard Thread Group
Thread counts for common test patterns
Custom Thread Groups from plugins
The standard Thread Group ramps linearly. Real traffic rarely does. The plugins installed in the previous lesson add more realistic shapes.
Stepping Thread Group — adds threads in steps with configurable hold time:
- Start with N threads
- Add M threads every X seconds
- Hold for D seconds before stepping again
- Stop adding at maximum N threads
This produces a staircase ramp that is more realistic than linear and makes it easier to observe server behaviour at each step before adding more load.
Ultimate Thread Group — defines the load shape as a series of rows, each specifying a start threads count, ramp-up time, hold time, shutdown time, and start delay. This gives you complete control to model any traffic pattern — morning ramp-up, lunch peak, afternoon plateau, evening decline.
Concurrency Thread Group (from the Throughput Shaping plugin) — targets a concurrent user count rather than a thread count. It adds and removes threads automatically to maintain the target concurrency even as threads complete or fail. More accurate for long tests where thread lifetimes vary.
Thread Groups and multiple scenarios
You can add multiple Thread Groups to a single test plan. By default they run in parallel. This is how you mix user types:
Test Plan
├── Thread Group — "Browse" (60 users, Infinite, 30min duration)
├── Thread Group — "Checkout" (20 users, Infinite, 30min duration)
└── Thread Group — "Admin" (5 users, Infinite, 30min duration)
Each Thread Group runs its own sampler tree independently. To run Thread Groups sequentially instead of in parallel, check Run Thread Groups consecutively in the Test Plan root configuration.
⚠️ Common mistakes
- Setting ramp-up to 0 for all tests. Starting all threads simultaneously generates an instant traffic spike that rarely matches real user arrival patterns. This is useful for spike testing intentionally but distorts load test results. Match your ramp-up period to a realistic user arrival rate — for a typical load test, ramp-up should be 10–30% of your total test duration.
- Using Loop Count instead of Duration for time-based tests. If response times vary (as they always do under load), a fixed loop count produces a test that runs for different durations on different runs. Use the Specify Thread Lifetime → Duration option for repeatable, time-bounded tests.
- Confusing JMeter threads with HTTP connections. A single JMeter thread can maintain a persistent HTTP keep-alive connection across multiple requests in one iteration. Thread count ≠ connection count. HTTP Request samplers use keep-alive by default — one thread reuses one connection for multiple requests within an iteration.
🎯 Practice task
Experiment with Thread Group configuration to see how it affects test execution.
- Open the
first-test.jmxyou built in the previous lesson. - Configure the Thread Group: 10 threads, 10-second ramp-up, loop count 3. Run it. Note the total number of results in View Results Tree — should be 30 (10 × 3).
- Change to a 30-second duration instead of loop count. Run again and note how many iterations completed in 30 seconds.
- Add a second Thread Group with 5 threads, loop count 2, and a different HTTP Request targeting
/crocodiles/. Run. Confirm both Thread Groups execute in parallel — you should see two sampler labels in View Results Tree. - If you installed the Stepping Thread Group plugin: replace the standard Thread Group with a Stepping Thread Group. Configure it to start with 1 thread, add 2 threads every 5 seconds, up to a maximum of 10. Watch the status bar thread count increment as the test runs.