A class whose instances may be pooled, cached, or treated as interchangeable by the runtime. Using one of these objects as a lock is dangerous because unrelated code can end up synchronising on the same underlying object, creating hidden contention or deadlock risk.
What Value-Based Classes Are
Value-based classes are designed to behave like interchangeable values rather than stable, identity-bearing objects. That means two equal instances may be pooled, cached, or reused by the runtime, so code must not assume that object identity, uniqueness, or referential stability is preserved.
Why Value-Based Classes Are Dangerous as Locks
The key hazard is that a value-based object can be shared behind the scenes. If unrelated code synchronises on such an object, it may accidentally block on the same underlying instance, creating hidden contention, unexpected interference, or deadlock paths that are very hard to diagnose.
This is why locking should be tied to a private, dedicated monitor rather than to an object whose lifecycle, caching behaviour, or identity semantics are controlled by the runtime.
How Runtime Interchangeability Changes Synchronization Semantics
With ordinary objects, developers often rely on a stable one-object, one-monitor model. Value-based classes break that assumption: the runtime may collapse equal values into shared instances, or present different instances that are intentionally treated as equivalent. That is useful for memory efficiency and API design, but it is incompatible with using the object itself as a uniqueness boundary.
The practical consequence is that synchronisation on value-based objects can become coupled to implementation details outside the caller’s control. A lock that looks local may not be local at all, and a harmless-looking cache or pool can turn into an unexpected coordination point across code paths.
When to Treat a Value-Based Class as Non-Locking Material
Use value-based classes for representing data, comparing values, and enabling runtime optimisations, not for monitor ownership. If a class is documented as value-based, treat its instances as disposable carriers of state, not as objects whose identity should anchor mutual exclusion or wait/notify style coordination.
Where a design needs synchronisation, use a separate private lock object or a higher-level concurrency construct whose identity and scope are explicitly under application control. That preserves the benefits of value-based semantics without inheriting the contention risk.
Risk and Threat Considerations
Synchronising on value-based classes can create subtle concurrency failures because code outside your component may observe the same underlying object or an equivalent runtime-managed instance. The result is hidden contention, unexpected serialization, or deadlock conditions that emerge only under load or specific caching behaviour.
Failure mechanism: The runtime pools, caches, or canonicalises equal values, so unrelated code contends on a shared monitor that was assumed to be private.
Impact: Application throughput can collapse, lock ordering assumptions can fail, and concurrency bugs can become intermittent, environment-dependent, and extremely difficult to reproduce.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
NIST SP 800-53 Rev 5 sets the technical controls, while ISO/IEC 27001:2022 defines the regulatory obligations.
| Framework | Control / Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev 5 | AC-6 — Least Privilege | Value-based locking should avoid unintended shared access paths. |
| SC-10 — Network Disconnect | Concurrency hazards can be reduced by isolating shared coordination points. | |
| Recommendation — Use private lock objects to enforce least-privilege access to monitor ownership. Isolate synchronization points so unrelated code cannot contend on the same monitor. | ||
| ISO/IEC 27001:2022 | A.8.24 — Use of cryptography | Supports disciplined use of technical mechanisms where stable control matters. |
| Recommendation — Separate stable control primitives from interchangeable value objects. | ||
Practitioner Guidance
What to watch for: Review any code that uses value-based classes in synchronized blocks, wait sets, or other monitor-based coordination. The safe pattern is to separate value representation from locking responsibility so that the lock object remains stable, private, and explicitly owned.
Practitioner takeaway: If a type is value-based, treat identity as an implementation detail, not as a concurrency primitive.