Join our Newsletter — 33% off our NHI Course

Double-checked locking

Double-checked locking is a lazy initialisation pattern that tries to avoid synchronizing every call by checking a value before and after a lock. It is only safe when publication is correctly ordered, which is why missing memory visibility guarantees can expose partially constructed objects.

Expanded Definition

Double-checked locking is a concurrency pattern used to reduce synchronization overhead during lazy initialisation. The core idea is simple: check whether an object exists before locking, then check again inside the lock before creating it. In practice, the pattern only works when the language runtime and memory model guarantee safe publication and visibility, so that other threads never observe a partially constructed object.

Definitions vary across vendors and programming ecosystems because the pattern is not a security control in itself, but a correctness pattern that can affect reliability, availability, and the integrity of security-sensitive state. In modern Java, C# and similar environments, the safe use of double-checked locking depends on specific language features such as volatile semantics or equivalent ordering guarantees. The distinction matters because a design that appears efficient in code review can still fail under contention if object construction and publication are not properly ordered. Guidance from the NIST Cybersecurity Framework 2.0 is relevant here because dependable system behaviour underpins secure operations.

The most common misapplication is treating double-checked locking as universally safe, which occurs when developers assume the lock alone prevents visibility bugs without verifying the runtime’s memory model.

Examples and Use Cases

Implementing double-checked locking rigorously often introduces language-specific constraints, requiring organisations to weigh faster steady-state reads against the complexity of proving that publication is safe.

  • A security service lazily creates a shared key cache only when the first request arrives, avoiding repeated locking on every lookup.
  • An application initialises a policy engine once, then reuses it across threads, but only after confirming that the object reference cannot become visible before construction completes.
  • A background agent loads a model client or connector on demand, where the same pattern must be paired with correct memory barriers to avoid inconsistent tool state.
  • A web platform defers creation of a heavy configuration object until startup traffic actually needs it, reducing latency while preserving deterministic behaviour.

For implementation guidance, engineers often cross-check language documentation and secure coding references such as the OWASP guidance on singleton and lazy initialisation patterns, especially when the object being initialised holds secrets, cryptographic material, or trust decisions. If the object is part of identity flows, the same caution applies to token validation caches, session guards, and NHI-related service clients that must never be observed in a half-built state.

Why It Matters for Security Teams

Security teams care about double-checked locking because concurrency defects can become operational security defects. A partially constructed object may bypass expected validation, corrupt access decisions, or trigger intermittent failures that are hard to reproduce during testing. In identity-heavy systems, the risk increases when the object encapsulates authentication state, policy evaluation, secrets handling, or agent tool access, because a race condition can quietly undermine the assumptions behind otherwise well-designed controls.

This issue is not just theoretical. The governance lesson from frameworks such as OWASP guidance for agentic and AI application risks is that tool-using software must be predictable before it is trusted with execution authority. Double-checked locking becomes relevant in these environments when a cache, client, or control object is initialised lazily and then reused by multiple threads or agents. The result is often a defect that survives unit tests and appears only under load.

Organisations typically encounter data corruption, authentication anomalies, or intermittent denial of service only after production concurrency exposes the flaw, at which point double-checked locking becomes operationally unavoidable to address.

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

OWASP Agentic AI Top 10 and OWASP Non-Human Identity Top 10 address the attack and risk surface, while NIST CSF 2.0, NIST SP 800-63 and NIST AI RMF set the governance and control requirements practitioners need to meet.

Framework Control / Reference Relevance
NIST CSF 2.0 PR.AC-4 Least-privilege and access integrity depend on reliably published state.
NIST SP 800-63 Identity systems rely on correct state handling during authentication and session flows.
OWASP Agentic AI Top 10 Agentic systems require predictable object state before tool execution is trusted.
NIST AI RMF AI risk management expects dependable system behaviour and controlled state transitions.
OWASP Non-Human Identity Top 10 NHI components often cache credentials or clients that must not be partially built.

Ensure identity-related caches and validators are safely initialised before processing credentials.