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:

\[ X = \frac{N}{R + Z} \]

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.

flowchart TD Q1{"Pause models a human reading or typing?"} Q1 -->|yes| Q2{"Same pause every time (debugging, fixed pacing)?"} Q2 -->|yes| CT["Constant Timer"] Q2 -->|no| Q3{"Known min and max, no preferred value?"} Q3 -->|yes| UT["Uniform Random Timer"] Q3 -->|no| GT["Gaussian Random Timer (mean = offset, spread = deviation)"] Q1 -->|no| Q4{"Many users must hit one request at once?"} Q4 -->|yes| ST["Synchronizing Timer"] Q4 -->|no| Q5{"Target is a request rate rather than a pause?"} Q5 -->|yes| TT["Constant / Precise Throughput Timer or Open Model Thread Group"] Q5 -->|no| JT["JSR223 Timer (custom distribution or pacing)"]

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.

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 .jmx file all three store them in the same two properties, ConstantTimer.delay and RandomTimer.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.

testname attribute

No

Thread Delay (in milliseconds)

Number of milliseconds to pause. Accepts variables and functions.

ConstantTimer.delay

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.

testname attribute

No

Random Delay Maximum (in milliseconds)

Maximum random number of milliseconds to pause (added on top of the offset).

RandomTimer.range

Yes

Constant Delay Offset (in milliseconds)

Number of milliseconds to pause in addition to the random delay.

ConstantTimer.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.

testname attribute

No

Deviation (in milliseconds)

Standard deviation of the random part, in milliseconds.

RandomTimer.range

Yes

Constant Delay Offset (in milliseconds)

Number of milliseconds to pause in addition to the random delay; this is the mean of the delay.

ConstantTimer.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.

testname attribute

No

Lambda (in milliseconds)

Lambda value in milliseconds: the mean of the Poisson part.

RandomTimer.range

Yes

Constant Delay Offset (in milliseconds)

Number of milliseconds to pause in addition to the random delay.

ConstantTimer.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 Label.

testname attribute

No

Language (Script language)

The scripting language, for example groovy.

scriptLanguage

Yes

Parameters

Parameters passed to the script, exposed as Parameters (one string) and args (string array split on white space).

parameters

No

Script file (File Name)

A script file; overrides the inline script. A relative path is resolved against the user.dir system property. Available to the script as FileName.

filename

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 ${...} references; read variables with vars.get().

cacheKey

No

Script

The inline script. Its return value is the delay in milliseconds.

script

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 &gt; 0 ? args[0] as double : 3000d
long capMs = args.length &gt; 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.

testname attribute

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.

groupSize

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.

timeoutInMs

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 users is 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

POST /api/auth/login

none

0 ms

GET /api/books?query=${term}

Gaussian Random Timer, offset 3000, deviation 1000

3000 ms

GET /api/books/${bookId}

Uniform Random Timer, offset 4000, maximum 2000

5000 ms

POST /api/loans

Constant Timer, ${__P(loanThinkMs,2000)}

2000 ms

GET /api/loans/${loanId}

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>

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>

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

think_time_creator.impl

Implementation of org.apache.jmeter.gui.action.thinktime.ThinkTimeCreator that builds the think-time structure.

org.apache.jmeter.thinktime.DefaultThinkTimeCreator

think_time_creator.default_timer_implementation

GUI class of the timer added by the default creator.

org.apache.jmeter.timers.gui.UniformRandomTimerGui

think_time_creator.default_constant_pause

Constant pause of the timer, in milliseconds.

1000

think_time_creator.default_range

Random range of the timer, in milliseconds.

100

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.

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.factor does 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.

timer.factor since 3.1 for the three random timers.

Think-time helpers

Manual placement of timers.

Add Think Times to children and the think_time_creator.* properties since 3.2; disabled elements skipped since 4.0; Think Time at a point merge template and Recording with Think Time template.

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.

References