Timers & Think Time
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. |
The reference material consulted while preparing these pages is listed in the section bibliography.
This page was written against Apache JMeter 5.6.3 (latest release on 2026-10-14; it runs on Java 8 and later, Java 17
or later is recommended), with the differences planned for JMeter 6.0 noted where they matter. JMeter 6.0 is not
released: the planned changes come from xdocs/changes.xml on the master branch, need Java 17 or later (Java 21
recommended) and are described as planned, not as shipped behaviour. The JSR223 Timer examples use the Groovy engine
bundled with 5.6.3 (Groovy 5 is planned for 6.0). The books on the market target JMeter 2.11 to 3.0; the four classic
timers they describe are unchanged, while timer.factor, the think-time templates, the Add Think Times to children
action and the Precise Throughput Timer arrived later. Every field name, property and rule on this page is taken from the
official component reference, properties reference and change log, and where the documentation is silent, from the
5.6.3 source code, which is said explicitly.
A JMeter thread runs its samplers back to back. Without a timer, one virtual user fires its next request the instant the previous response arrives, which no human does. Timers put the pauses back. They decide how realistic the traffic is, how many threads a target load needs, and whether a spike happens at all. This page covers the timers that model a user’s pause (think time) and the Synchronizing Timer that creates spikes; the throughput timers that pace load to a rate (Constant Throughput and Precise Throughput) are introduced in the comparison below and treated in full on Open Model & Throughput Control. The running example is the Bookshelf API journey used throughout the section: log in, search, view a book, create a loan, view the loan.
Why think time matters
Realism
A real Bookshelf user reads the search results before opening a book and reads the book page before borrowing it. Those pauses are the think time. They change the traffic in three ways that matter for the server:
-
Concurrency on the server. With think time, most virtual users are idle at any moment, so the number of requests in flight is much smaller than the number of users, as it is in production. Without it, every thread is a request in flight, and 50 threads behave like 50 permanently busy clients.
-
Session and cache lifetime. Sessions, tokens, connection pools and caches see users who stay around for minutes and come back, not users who hammer them in a tight loop. Idle-timeout bugs and session-store growth only show up when the pauses are realistic.
-
Burstiness. Random think times spread requests out and let them cluster naturally; a fixed delay makes every thread march in step after a ramp-up, which produces artificial waves.
The JMeter manual states the basic reason plainly in its description of timers: by default a thread executes samplers in sequence without pausing, and without a delay JMeter "could overwhelm your server" with too many requests in a short time.
The Little’s-Law maths
In a closed model (a Thread Group with a fixed number of threads) the throughput is set by the users, the response time and the think time together. Performance Testing Fundamentals derives the relation:
where \(X\) is the throughput in requests per second, \(N\) the number of threads, \(R\) the average response time and \(Z\) the average think time per request, both in seconds. Two consequences drive every timer decision on this page:
-
Think time sets the thread count. At 200 ms response time, 20 requests per second need 20 x 0.2 = 4 threads with no think time and 20 x 3.2 = 64 threads with a 3 s think time. The second is the realistic workload; the first is a different test.
-
The mean of the delay is what counts for throughput; its spread is what counts for realism. A Uniform Random Timer from 2 s to 4 s and a Constant Timer of 3 s give the same throughput, but very different arrival patterns. This is why the comparison table below gives the mean and spread of every timer.
The JMeter manual does not state Little’s Law; the derivation and its limits (steady state, averages only) are on the fundamentals page.
Where timers sit and when they run
Execution order and scope
Timers are hierarchical elements. The component reference sets the rules:
-
Timers are processed before each sampler in the scope in which they are found. The execution order for one sampler is configuration elements, pre-processors, timers, the sampler, then post-processors, assertions and listeners.
-
If several timers are in the scope of one sampler, all of them are processed before it and the thread sleeps for the sum of their delays.
-
Timers are only processed in conjunction with a sampler. A timer that is not in the scope of any sampler is never processed.
-
To apply a timer to a single sampler, add it as a child of that sampler. It still runs before that sampler.
-
To pause after a sampler, add the timer to the next sampler, or add it as the child of a Flow Control Action sampler.
The general scoping rules, including a worked table of timers adding up across levels, are on Test Plan Elements, Execution Order & Scoping. What follows is how those rules translate into placement decisions for think time.
Placement patterns
| Placement | Effect | Use it for |
|---|---|---|
Timer directly under the Thread Group |
Applied before every sampler in the group, including login and any helper samplers (Debug Sampler, Dummy Sampler). |
A uniform "pause between any two clicks" in a simple plan. Easy to forget when timers are added lower down, since they then add up. |
Timer under a logic controller |
Applied before every sampler inside that controller, nowhere else. |
A pause that belongs to one part of the journey, for example a longer reading time for every page in a Browse controller. |
Timer as a child of one sampler |
Applied only before that sampler. |
Per-step think time: the time a user spends on the previous page before triggering this request. The most precise and most common pattern. |
Flow Control Action (Pause, duration 0) with a timer as its child |
A pause at one exact point in the tree, with no sample written to the results. |
A pause after a request, at the end of an iteration, or inside a Transaction Controller without adding a fake sample. This is what the Think Time at a point template and Add Think Times to children create. |
The Flow Control Action is a sampler, so a timer under it has a sampler to run with, yet it produces no result. The component reference says so directly: for variable delays, set its pause time to zero and add a timer as a child.
Timer comparison
The table summarises every timer shipped with JMeter 5.6.3. The delay formula column uses the GUI field names;
"offset" is Constant Delay Offset. Where the component reference describes the formula in words, the exact
expression comes from the 5.6.3 source code of the timer classes (UniformRandomTimer, GaussianRandomTimer,
PoissonRandomTimer), which also take the absolute value of the result so that a delay is never negative.
| Timer | Delay per sampler | Distribution | Mean and spread | timer.factor |
Typical use |
|---|---|---|---|---|---|
Constant Timer |
Thread Delay |
None (a single value) |
Mean = Thread Delay; spread 0 |
No |
Debugging, a fixed minimum pause, deterministic pacing between steps. |
Uniform Random Timer |
offset + U x Random Delay Maximum, with U uniform in [0, 1) |
Flat (uniform) between offset and offset + maximum |
Mean = offset + maximum / 2; standard deviation = maximum divided by the square root of 12 (about 0.29 x maximum) |
Yes |
Think time with known bounds and no typical value ("between 2 and 4 s"). |
Gaussian Random Timer |
\(\lvert\) offset + G x Deviation \(\rvert\), with G normal with mean 0 and standard deviation 1 |
Bell curve centred on the offset |
Mean = offset; standard deviation = Deviation; about 68 % of delays within one deviation and 95 % within two |
Yes |
Human think time clustered around a typical value ("about 3 s, give or take 1 s"). |
Poisson Random Timer |
offset + P, with P Poisson distributed with mean Lambda |
Poisson around Lambda, discrete |
Mean = offset + Lambda; standard deviation = the square root of Lambda, in ms (Lambda 3000 gives about 55 ms) |
Yes |
Rarely the right choice for think time: the spread is tiny compared with the mean. The manual itself points to the Precise Throughput Timer to model Poisson arrivals. |
JSR223 Timer |
Whatever the script returns, converted to a long number of milliseconds |
Any (defined by the script) |
Defined by the script |
No |
Custom distributions (exponential, log-normal, from a CSV of measured pauses), pacing, conditional pauses. |
BeanShell Timer |
Whatever the script returns, in milliseconds |
Any |
Defined by the script |
No |
Legacy plans only; migrate to the JSR223 Timer with Groovy. |
Synchronizing Timer |
Blocks until Number of Simultaneous Users to Group by threads are waiting (or the timeout expires), then releases them together; adds no delay of its own |
Not a distribution: a barrier |
Waiting time depends on how fast threads arrive |
No |
Spikes: many users hitting one request at the same instant. |
Constant Throughput Timer |
Computed so that the throughput approaches Target Throughput (samples per minute) |
Even intervals: it "tends to produce samples at even intervals" |
Mean interval = 60 / target, per thread or shared, depending on Calculate Throughput based on |
No |
Pacing a closed model to a rate. Full treatment on the open-model page. |
Precise Throughput Timer |
Computed from a schedule built at test start, Target throughput per Throughput period |
Poisson arrivals (exponential gaps between samples), with an exact total count over Test duration |
Mean rate = target / period |
No |
Rate-based load with realistic randomness; often the Open Model Thread Group is the better choice. |
Source: Timers (Component Reference)
Three points from the table are worth stating separately:
-
Uniform, Gaussian and Poisson share their fields. All three have Constant Delay Offset plus one random parameter (Random Delay Maximum, Deviation, Lambda), and in the
.jmxfile all three store them in the same two properties,ConstantTimer.delayandRandomTimer.range. Switching one for another is a matter of changing the element type, but the meaning of the second number changes. -
The Poisson Random Timer is not "random arrivals". A Poisson distribution with mean \(\lambda\) has variance \(\lambda\), so a 3000 ms Lambda varies by about 55 ms: practically a Constant Timer. Exponentially distributed gaps, the ones that produce Poisson arrivals, come from the Precise Throughput Timer, the Open Model Thread Group’s
random_arrivals(), or a JSR223 Timer such as the one below. -
The Gaussian folds at zero. Because the source code takes the absolute value, a Gaussian Random Timer whose deviation is close to the offset produces a distorted distribution with extra short delays instead of negative ones. Keep the deviation at a third of the offset or less so that negative values are rare (below about 0.15 %).
Worked numbers for the comparison
With an offset of 2000 ms on each random timer:
| Configuration | Range of delays | Mean | Standard deviation |
|---|---|---|---|
Constant Timer, Thread Delay 3000 |
3000 ms |
3000 ms |
0 ms |
Uniform Random Timer, offset 2000, maximum 2000 |
2000 ms to 4000 ms |
3000 ms |
about 577 ms |
Gaussian Random Timer, offset 3000, deviation 500 |
95 % between 2000 ms and 4000 ms |
3000 ms |
500 ms |
Poisson Random Timer, offset 0, Lambda 3000 |
almost all between 2840 ms and 3160 ms |
3000 ms |
about 55 ms |
All four rows give the same throughput in a closed model; only the shape of the traffic differs.
The timers in detail
Each timer is added through Add → Timer on the right-click menu of a Thread Group, a controller or a sampler.
Constant Timer
Pauses each thread for the same time before every sampler in its scope.
| GUI field | Meaning | JMX property | Required |
|---|---|---|---|
Name |
Name shown in the tree. |
|
No |
Thread Delay (in milliseconds) |
Number of milliseconds to pause. Accepts variables and functions. |
|
Yes |
The Constant Timer is not scaled by timer.factor (the property applies only to the three random timers), so it is
the right timer for a fixed delay that must not change when think time is tuned globally. A property-driven value such
as ${__P(loanThinkMs,2000)} lets the CLI change it without editing the plan. The default hotkey in the GUI for adding
a Constant Timer is Ctrl + 4 (from the hints and tips page; gui.quick_* properties change it).
<ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="Think before loan" enabled="true">
<stringProp name="ConstantTimer.delay">${__P(loanThinkMs,2000)}</stringProp>
</ConstantTimer>
<hashTree/>
Uniform Random Timer
Pauses for a random time, each value in the interval being equally likely. The total delay is the random value plus the offset.
| GUI field | Meaning | JMX property | Required |
|---|---|---|---|
Name |
Name shown in the tree. |
|
No |
Random Delay Maximum (in milliseconds) |
Maximum random number of milliseconds to pause (added on top of the offset). |
|
Yes |
Constant Delay Offset (in milliseconds) |
Number of milliseconds to pause in addition to the random delay. |
|
Yes |
For a pause between 4 s and 6 s, set the offset to 4000 and the maximum to 2000. The mean is 5000 ms. A frequent mistake is to enter the upper bound (6000) as the maximum, which gives 4 s to 10 s and a mean of 7 s.
<UniformRandomTimer guiclass="UniformRandomTimerGui" testclass="UniformRandomTimer" testname="Read book page" enabled="true">
<stringProp name="ConstantTimer.delay">4000</stringProp>
<stringProp name="RandomTimer.range">2000.0</stringProp>
</UniformRandomTimer>
<hashTree/>
Gaussian Random Timer
Pauses for a random time with most values near a particular value. The manual describes the total delay as a Gaussian value (mean 0.0, standard deviation 1.0) multiplied by the deviation, plus the offset: the variation around the constant offset follows a bell curve.
| GUI field | Meaning | JMX property | Required |
|---|---|---|---|
Name |
Name shown in the tree. |
|
No |
Deviation (in milliseconds) |
Standard deviation of the random part, in milliseconds. |
|
Yes |
Constant Delay Offset (in milliseconds) |
Number of milliseconds to pause in addition to the random delay; this is the mean of the delay. |
|
Yes |
Note the asymmetry with the Uniform Random Timer: here the offset is the centre of the distribution, not its lower bound. An offset of 3000 and a deviation of 1000 gives delays mostly between 1 s and 5 s, centred on 3 s.
<GaussianRandomTimer guiclass="GaussianRandomTimerGui" testclass="GaussianRandomTimer" testname="Read search results" enabled="true">
<stringProp name="ConstantTimer.delay">3000</stringProp>
<stringProp name="RandomTimer.range">1000.0</stringProp>
</GaussianRandomTimer>
<hashTree/>
Poisson Random Timer
Pauses for a random time with most values near a particular value: the sum of a Poisson-distributed value and the offset. The component reference adds that to model Poisson arrivals, the Precise Throughput Timer should be used instead.
| GUI field | Meaning | JMX property | Required |
|---|---|---|---|
Name |
Name shown in the tree. |
|
No |
Lambda (in milliseconds) |
Lambda value in milliseconds: the mean of the Poisson part. |
|
Yes |
Constant Delay Offset (in milliseconds) |
Number of milliseconds to pause in addition to the random delay. |
|
Yes |
<PoissonRandomTimer guiclass="PoissonRandomTimerGui" testclass="PoissonRandomTimer" testname="Poisson pause" enabled="true">
<stringProp name="ConstantTimer.delay">0</stringProp>
<stringProp name="RandomTimer.range">3000.0</stringProp>
</PoissonRandomTimer>
<hashTree/>
JSR223 Timer
Generates a delay with a script in any JSR223 language. The script’s return value is converted to a long integer and used as the number of milliseconds to wait. Use Groovy, the default language since 3.1, with compilation caching.
| GUI field | Meaning | JMX property | Required |
|---|---|---|---|
Name |
Name shown in the tree; available to the script as |
|
No |
Language (Script language) |
The scripting language, for example |
|
Yes |
Parameters |
Parameters passed to the script, exposed as |
|
No |
Script file (File Name) |
A script file; overrides the inline script. A relative path is resolved against the |
|
No |
Script compilation caching (Cache compiled script if available) |
Caches the compiled script when the language supports it (Groovy does; Java, BeanShell and JavaScript do not). The
script must then not contain |
|
No |
Script |
The inline script. Its return value is the delay in milliseconds. |
|
Yes, unless a script file is given |
The bindings available to the script are log, ctx, vars, props, sampler (the sampler the timer runs before),
Label, FileName, Parameters, args and OUT. Per the 5.6.3 source code, a script that returns nothing (null)
gives a delay of 0 and a warning in jmeter.log; a value that is not a number is logged as an error and also gives 0.
The size of the compiled-script cache is the property jsr223.compiled_scripts_cache_size (default 100).
Example: exponential think time. Measured think times are usually skewed: many short pauses and a long tail. An exponential distribution with a cap is a simple approximation that none of the built-in think-time timers provides. The mean is passed as a parameter so the same script serves several steps.
import java.util.concurrent.ThreadLocalRandom
// Parameters: "<meanMs> <capMs>", for example "3000 15000"
double meanMs = args.length > 0 ? args[0] as double : 3000d
long capMs = args.length > 1 ? args[1] as long : 15000L
double u = ThreadLocalRandom.current().nextDouble() // uniform in [0, 1)
long delay = Math.round(-meanMs * Math.log(1d - u)) // exponential with the given mean
return Math.min(delay, capMs)
<JSR223Timer guiclass="TestBeanGUI" testclass="JSR223Timer" testname="Exponential think time" enabled="true">
<stringProp name="scriptLanguage">groovy</stringProp>
<stringProp name="parameters">3000 15000</stringProp>
<stringProp name="filename"></stringProp>
<stringProp name="cacheKey">true</stringProp>
<stringProp name="script">import java.util.concurrent.ThreadLocalRandom
double meanMs = args.length > 0 ? args[0] as double : 3000d
long capMs = args.length > 1 ? args[1] as long : 15000L
double u = ThreadLocalRandom.current().nextDouble()
long delay = Math.round(-meanMs * Math.log(1d - u))
return Math.min(delay, capMs)</stringProp>
</JSR223Timer>
<hashTree/>
With a mean of 3000 ms and a cap of 15000 ms, the cap cuts off \(e^{-5}\), about 0.7 % of the draws, so the effective mean is slightly below 3 s (about 2.98 s). The cost on the load generator is one script evaluation per sampler in scope; with caching enabled that is a cheap method call, without it Groovy compiles the script every time, which is expensive at hundreds of requests per second. The scripting page of this section covers JSR223 caching in depth.
BeanShell Timer
Generates a delay from a BeanShell script, with the fields Reset bsh.Interpreter before each call, Parameters,
Script file and Script, and an initialisation file named by the property beanshell.timer.init. The component
reference recommends migrating to the JSR223 Timer with Groovy "for performance, support of new Java features and
limited maintenance of the BeanShell library". New plans should not use it; the Groovy example above is the
replacement.
Synchronizing Timer
The Synchronizing Timer blocks threads until a given number of them are waiting, then releases them all at once. It can thus create large instant loads at chosen points of the plan: the classic way to build a spike inside a closed-model test.
| GUI field | Meaning | JMX property | Required |
|---|---|---|---|
Name |
Name shown in the tree. |
|
No |
Number of Simultaneous Users to Group by (the 5.6.3 GUI labels it Number of Simulated Users to Group by) |
Number of threads to release at once. 0 means the number of threads in the Thread Group. |
|
Yes |
Timeout in milliseconds |
0: wait until the group is complete, however long that takes. Greater than 0: wait at most this long; if the group is still incomplete, stop waiting. Default 0. |
|
No |
<SyncTimer guiclass="TestBeanGUI" testclass="SyncTimer" testname="Spike on search" enabled="true">
<intProp name="groupSize">50</intProp>
<longProp name="timeoutInMs">15000</longProp>
</SyncTimer>
<hashTree/>
How it behaves, from the component reference and the 5.6.3 source (SyncTimer uses a CyclicBarrier shared by the
threads of the group):
-
A forever wait is possible. With a timeout of 0, if the number of waiting threads never reaches the group size (for example 100 threads that are not a multiple of a group of 30, or threads that stopped on an error), the test pauses indefinitely and only a forced stop ends it. Always set a timeout.
-
On timeout, the waiting threads go on together. When the timeout expires the barrier is broken, every waiting thread proceeds without further delay, a warning
SyncTimer ... timeouted waiting for usersis logged, and the barrier is reset for the next group. The burst is then smaller than the group size. -
It respects the scheduler. Since 5.0 (Bug 62637), the wait is cut short so that it does not extend beyond the Thread Group’s scheduled end.
-
One JVM only. The barrier lives in one JMeter process. In distributed testing each injector forms its own groups, so the group size must not exceed the number of threads of the Thread Group on one injector; three injectors with a group of 50 give three bursts of 50, not one of 150, and the three are not aligned in time.
-
Placement. As a timer it applies to every sampler in its scope; put it as the child of the one sampler that must be hit at once, never at Thread Group level, or every request of the journey becomes a synchronised burst.
Worked Bookshelf spike. A Thread Group of 100 threads with a 20 s ramp-up starts 5 threads per second. A
Synchronizing Timer as the child of GET /api/books?query=${term} with a group of 50 and a timeout of 15000 ms
behaves as follows on the first pass:
-
the first 50 threads reach the search within 50 / 5 = 10 s of the start, plus the login time;
-
at that moment the barrier releases all 50, and the Bookshelf API receives 50 search requests within a few milliseconds, whereas the same 50 users without the timer would arrive at about 5 per second;
-
the next 50 threads form the second group over the following 10 s.
If the timeout were 5000 ms instead, only about 25 threads (5 s x 5 threads/s) would be waiting when the first one times out, and the burst would halve. Choose the timeout larger than the time the group needs to form, which is the group size divided by the arrival rate of threads at that sampler. After the first pass, threads return to the search at times spread by their think times and response times, so later bursts form more slowly; for repeated, precisely shaped spikes, the Ultimate Thread Group of the Custom Thread Groups plugin or an Open Model Thread Group schedule with a short high-rate step are more controllable (both are covered later in this section).
Constant Throughput Timer and Precise Throughput Timer
These two timers do not model a person’s pause; they compute whatever pause is needed to reach a rate. They are listed in the comparison table for completeness. Their fields are:
-
Constant Throughput Timer: Target Throughput (in samples per minute) and Calculate Throughput based on (this thread only; all active threads in current thread group; all active threads; all active threads in current thread group (shared); all active threads (shared)).
-
Precise Throughput Timer: Target throughput (in samples per 'throughput period'), Throughput period (seconds), Test duration (seconds), Number of threads in the batch (threads), Delay between threads in the batch (ms) and Random seed (change from 0 to random).
Both can only slow threads down: they cannot exceed what the threads can deliver, so the closed-model maths above still applies. Combining one with think-time timers in the same scope adds the think time on top of the computed pause, which lowers the rate the timer can reach. The component reference makes the same point for the Precise Throughput Timer: the main driver for think times and the number of threads should be business requirements, not the desire to match a throughput. How to configure them, their calculation modes, the dummy-sampler placement and a worked 20 requests per second example are on Open Model & Throughput Control.
Think time in the Bookshelf plan
The journey with per-step think time
Each think time is attached as a child of the request that follows the pause, so it models the time the user spends on the previous page:
| Sampler | Timer (child of the sampler) | Mean delay |
|---|---|---|
|
none |
0 ms |
|
Gaussian Random Timer, offset 3000, deviation 1000 |
3000 ms |
|
Uniform Random Timer, offset 4000, maximum 2000 |
5000 ms |
|
Constant Timer, |
2000 ms |
|
Constant Timer, 1000 |
1000 ms |
The resulting test plan fragment (HTTP Request bodies, extractors and assertions are omitted; they are built on the HTTP, correlation and assertion pages):
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET /api/books?query" enabled="true">
<stringProp name="HTTPSampler.path">/api/books?query=${term}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>
<hashTree>
<GaussianRandomTimer guiclass="GaussianRandomTimerGui" testclass="GaussianRandomTimer" testname="Read search results" enabled="true">
<stringProp name="ConstantTimer.delay">3000</stringProp>
<stringProp name="RandomTimer.range">1000.0</stringProp>
</GaussianRandomTimer>
<hashTree/>
</hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET /api/books/{id}" enabled="true">
<stringProp name="HTTPSampler.path">/api/books/${bookId}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>
<hashTree>
<UniformRandomTimer guiclass="UniformRandomTimerGui" testclass="UniformRandomTimer" testname="Read book page" enabled="true">
<stringProp name="ConstantTimer.delay">4000</stringProp>
<stringProp name="RandomTimer.range">2000.0</stringProp>
</UniformRandomTimer>
<hashTree/>
</hashTree>
</hashTree>
Source: Timers (Component Reference)
Worked numbers. This example deliberately uses a richer think-time model than the rest of the section: separate reading pauses per step (11 s per five-request journey) instead of the flat Z = 3 s per request behind the section-wide figure of about 15.6 requests per second for 50 threads (see the performance testing fundamentals and thread groups pages). The method is the same; only where the pauses sit differs. With five requests per iteration and an average response time of 200 ms each:
-
think time per iteration = 0 + 3000 + 5000 + 2000 + 1000 = 11000 ms;
-
response time per iteration = 5 x 200 = 1000 ms, so one iteration takes about 12 s;
-
one user issues 5 / 12 = 0.42 requests per second and completes 1 / 12 = 0.083 journeys per second;
-
the load profile of the section (50 users) therefore produces 50 x 0.42 = about 21 requests per second and 4.2 journeys per second at full load.
If the response time rises to 1 s under load, the iteration lengthens to 16 s and the same 50 users produce only 50 x 5 / 16 = about 16 requests per second: the closed model backs off as the server slows, which is exactly the behaviour discussed in the coordinated-omission section of the fundamentals page.
Scaling think time globally with timer.factor
Since 3.1, the property timer.factor multiplies the delay computed by the Gaussian, Uniform and Poisson Random Timers.
It does not affect the Constant Timer, the JSR223 or BeanShell Timers, the Synchronizing Timer or the throughput
timers. The component reference still describes it as a feature introduced in Beta mode in 3.1.
# user.properties: halve every random think time
timer.factor=0.5
Or on the command line, for one run:
jmeter -n -t bookshelf-load.jmx -l results/load-fast-think.jtl -Jtimer.factor=0.5
The output has the usual form (the numbers are illustrative):
Creating summariser <summary>
Created the tree successfully using bookshelf-load.jmx
Starting standalone test @ 2026 Oct 14 11:00:00 CEST (1791968400000)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
...
summary + 921 in 00:00:30 = 30.7/s Avg: 203 Min: 12 Max: 811 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
...
With timer.factor=0.5 in the Bookshelf plan, only the Gaussian and Uniform pauses are halved: 1500 + 2500 + 2000
1000 = 7000 ms of think time, an 8 s iteration and 50 x 5 / 8 = about 31 requests per second. This is why the loan
pauses use Constant Timers driven by ${__P(...)} properties: they stay fixed while the reading times are scaled.
Remember that halving think time doubles the per-user pressure; it is a way to reach a higher load with the same
thread count, not a neutral speed-up.
Timers and the scheduled end of a Thread Group
When the Thread Group has a duration (Specify Thread lifetime), JMeter avoids sleeping past the scheduled end. Per
the 5.6.3 source code of JMeterThread, if the total delay of the timers before a sampler is longer than the time
left, the thread stops immediately instead of sleeping and the sampler is not executed. This dates from 3.1 (Bug 60049:
"When using Timers with high delays or Constant Throughput Timer with low throughput, Scheduler may take a lot of time
to exit"). The practical effect is that the last iteration of a long-think-time test is cut short and its final
requests never appear in the results, which is expected and not an error.
Source: Changes history
Think time from templates and the GUI
Think Time at a point template
File → Templates… → Think Time at a point is a merge template (not a whole test plan): select the Thread Group or controller where the pause belongs, then create the template, and JMeter merges a Flow Control Action named ThinkTime, with action Pause and duration 0, with a Uniform Random Timer named URT as its child (offset 1000 ms, random maximum 100 ms). The template description calls it a think time of "1s+/- 100 ms between 2 samplers"; strictly, the delay is 1000 ms to 1100 ms. Move the Flow Control Action between the two samplers and adjust the timer.
<TestAction guiclass="TestActionGui" testclass="TestAction" testname="ThinkTime" enabled="true">
<intProp name="ActionProcessor.action">1</intProp>
<intProp name="ActionProcessor.target">0</intProp>
<stringProp name="ActionProcessor.duration">0</stringProp>
</TestAction>
<hashTree>
<UniformRandomTimer guiclass="UniformRandomTimerGui" testclass="UniformRandomTimer" testname="URT" enabled="true">
<stringProp name="ConstantTimer.delay">1000</stringProp>
<stringProp name="RandomTimer.range">100.0</stringProp>
</UniformRandomTimer>
<hashTree/>
</hashTree>
Source: Flow Control Action (Component Reference) and Templates
Add Think Times to children
Right-click a Thread Group or a logic controller and choose Add Think Times to children. JMeter inserts a think-time pair (a Flow Control Action in Pause mode with a timer as its child) after every enabled sampler and controller directly below the selected node. Since 4.0 (Bug 61629) disabled elements are skipped. What gets inserted is controlled by properties:
| Property | Meaning | Default |
|---|---|---|
|
Implementation of |
|
|
GUI class of the timer added by the default creator. |
|
|
Constant pause of the timer, in milliseconds. |
|
|
Random range of the timer, in milliseconds. |
|
To make the action insert Gaussian pauses of 3 s plus or minus 1 s instead, put this in user.properties before
starting the GUI:
think_time_creator.default_timer_implementation=org.apache.jmeter.timers.gui.GaussianRandomTimerGui
think_time_creator.default_constant_pause=3000
think_time_creator.default_range=1000
Recording with Think Time template
File → Templates… → Recording with Think Time creates a recording plan like the plain Recording template (HTTP(S)
Test Script Recorder on port 8888, a Recording Controller, a View Results Tree), but with a Uniform Random Timer placed
directly inside the recorder whose Constant Delay Offset is ${T} and whose Random Delay Maximum is 100. The
recorder copies a timer found inside it into each recorded sample (or into the first sample of each group when grouping
is used) and replaces every ${T} in its properties with the gap, in milliseconds, since the previous recorded
request. The recorded plan therefore replays with the pauses the person actually took while recording, plus up to
100 ms of jitter.
Two details matter when using it:
-
The recorded pauses include everything the person did between clicks, including looking away from the screen. Review them after recording and cap or replace outliers; a 40 s pause recorded because of a phone call becomes 40 s of think time for every virtual user.
-
Grouping of requests into "clicks" relies on the property
proxy.pause(default 5000 ms): requests closer together than that are treated as the same user action. With grouping, only the first sample of each group gets the timer, so embedded calls fired by one click are not delayed.
Recording itself (certificate, browser set-up, filtering) is covered on the recording page of this section.
Running without timers
Two GUI actions ignore timers on purpose:
-
Run → Start no pauses runs the whole plan with the timers removed, to check the logic quickly.
-
Validate on a Thread Group runs one thread for one iteration and ignores timers by default (
testplan_validation.ignore_timers=true).
Both are for building and debugging. A load test run with no pauses measures a different workload, as the Little’s-Law section above shows.
Source: Getting Started (User Manual) and Properties Reference
Timers inside Transaction Controllers
A Transaction Controller produces an extra sample that measures the time taken by its children. Its checkbox Include
duration of timer and pre-post processors in generated sample (default off since 2.11; JMX property
TransactionController.includeTimers) decides whether timer delays count:
-
Unchecked (default): the transaction time is the sum of the nested samplers' times. This is the user-perceived response time of the business step and is what SLAs usually refer to.
-
Checked: the transaction time includes all processing within the controller scope: timers, pre- and post-processors. Use it to measure the elapsed time of a whole user journey, think time included.
The GUI writes the property explicitly as false when it creates a Transaction Controller. Per the 5.6.3 source code
(TransactionControllerSchema), a .jmx that omits the property, such as a hand-written or generated plan, is read as
true, so write it explicitly when generating plans.
For the Bookshelf Borrow a book transaction (POST /api/loans with its 2000 ms Constant Timer, then
GET /api/loans/${loanId} with its 1000 ms Constant Timer), at 200 ms per request:
| Setting | Transaction time |
|---|---|
Include timers unchecked |
200 + 200 = about 400 ms |
Include timers checked |
2000 + 200 + 1000 + 200 = about 3400 ms |
A think time that should happen between two transactions belongs outside both of them, typically as a Flow Control Action with a timer between the two controllers; otherwise, with the checkbox on, it inflates one of them. The component reference also notes that the Flow Control Action is useful with the Transaction Controller precisely because it allows pauses without generating a sample.
<TransactionController guiclass="TransactionControllerGui" testclass="TransactionController" testname="Borrow a book" enabled="true">
<boolProp name="TransactionController.parent">false</boolProp>
<boolProp name="TransactionController.includeTimers">false</boolProp>
</TransactionController>
The Transaction Controller’s other option, Generate Parent Sample, and its interaction with listeners are covered on Logic Controllers & Transactions.
Cost on the load generator
-
Sleeping threads are cheap in CPU, not in memory. A thread in a timer sleeps; it uses no CPU, but it still holds its Java thread stack, its variables and its connections. Realistic think time multiplies the thread count needed for a given rate (64 instead of 4 threads in the example above), so heap and thread limits, not CPU, become the constraint. Size the injector from the thread count, as described on the thread group page.
-
The four classic timers cost almost nothing per call: one random number and an addition.
-
JSR223 Timers run a script before every sampler in scope. Enable compilation caching, keep the script short, and put the timer as a child of the samplers that need it rather than at Thread Group level.
-
Synchronizing Timers park threads until the group is complete; parked threads cost memory only, but a misconfigured group can park the whole test.
-
Precise Throughput Timer keeps its schedule in memory: about 8 MB of heap per million scheduled samples according to the component reference.
Pitfalls
-
Forgotten timers add up. A timer left at Thread Group level adds to every per-sampler timer below it. Search the tree for timers before trusting the think time.
-
A timer with no sampler in scope does nothing. A timer placed as the last child of a controller with no sampler after it still applies to all samplers of that controller (order does not matter for hierarchical elements), but a timer under a controller that contains no samplers is never processed.
-
Timers run before the sampler. A timer as a child of the last request of an iteration delays that request, not the start of the next iteration. Use a Flow Control Action for a pause after it.
-
Uniform maximum is a range, not an upper bound. Offset 2000 and maximum 4000 gives 2 s to 6 s.
-
Gaussian deviation too large. Deviation close to or above the offset folds negative values into short delays and distorts the mean upwards.
-
Poisson Random Timer for "random arrivals". It produces nearly constant delays; use the Precise Throughput Timer, the Open Model Thread Group or an exponential JSR223 Timer.
-
Synchronizing Timer without a timeout can hang the test; with a timeout shorter than the group’s formation time it produces smaller bursts than expected; in distributed tests the bursts are per injector.
-
Removing think time to "get more load". It changes the workload; add threads, or use a rate-based model, instead.
-
Think time inside transactions. With Include duration of timer checked, transactions measure the user’s reading time, not the system’s response time.
-
timer.factordoes not scale everything. Constant, JSR223, Synchronizing and throughput timers ignore it.
From the books to 5.6 to 6.0
| Topic | Books (JMeter 2.11 to 3.0) | 5.6.3 and planned 6.0 |
|---|---|---|
Constant, Uniform, Gaussian, Poisson Random Timers |
Same fields and behaviour (the Poisson timer was added in 2.6). |
Unchanged in 5.6.3; nothing planned for 6.0. |
Synchronizing Timer |
Timeout added in 2.12. |
5.0 makes the wait respect the scheduler (Bug 62637). Unchanged for 6.0. |
Scripted timers |
BeanShell, BSF and JSR223 Timers (JSR223 Timer since 2.4). |
BSF deprecated in 3.1 (still shipped, hidden from the menus); Groovy is the default JSR223 language since 3.1; BeanShell discouraged. 6.0 plans Groovy 5 for JSR223 scripts. |
Global think-time scaling |
Not available; edit every timer or use properties. |
|
Think-time helpers |
Manual placement of timers. |
Add Think Times to children and the |
Timers and scheduled end |
Long delays could keep a test running past its duration. |
Since 3.1 (Bug 60049) delays are shortened, or the thread stops, at the scheduled end. |
Rate-based timers |
Constant Throughput Timer; Throughput Shaping Timer plugin. |
Precise Throughput Timer since 4.0 and Open Model Thread Group since 5.5. Planned for 6.0: Precise Throughput Timer delays measured from the start of the Thread Group instead of the start of the test, and a schema for the Constant Throughput Timer that ensures its required properties are initialised. |
Transaction Controller and timers |
Timers excluded by default since 2.11, with a checkbox to include them. |
Unchanged. |
Source: Changes history, Changes and changes.xml on master
References
-
Constant Timer, Uniform Random Timer, Gaussian Random Timer and Poisson Random Timer
-
Properties Reference (timer related feature, Thread Group validation, Test Script Recorder configuration, JSR223)
-
Apache JMeter source (5.6.3 timer classes,
bin/templates, andxdocs/changes.xmlon master for planned 6.0 changes) -
Bayo Erinle, Performance Testing with JMeter, 2nd ed., Packt, 2015 (JMeter 2.12), and Sai Matam and Jagdeep Jain, Pro Apache JMeter, Apress, 2017 (JMeter 3.0): background on think time and workload realism, used for concepts only.