Locking On A Value-Based Instance

ID

java.no_synchronize_on_value_based_class

Severity

high

Remediation Complexity

trivial

Remediation Risk

medium

Remediation Effort

low

Resource

Reliability

Language

Java

Tags

reliability

Description

Reports a synchronized block whose monitor’s declared type is a known value-based class: java.util.Optional and its primitive variants (OptionalInt, OptionalLong, OptionalDouble), the common java.time value types (LocalDate, LocalDateTime, LocalTime, Instant, Duration, Period, ZonedDateTime, OffsetDateTime, OffsetTime, Year, YearMonth, MonthDay), and the alternate-calendar date classes (HijrahDate, JapaneseDate, MinguoDate, ThaiBuddhistDate).

The match is against this explicit list, not a package-prefix heuristic — a java.time.format type such as DateTimeFormatter is not value-based and is not reported. A monitor whose declared type cannot be resolved from a declaration in the same file is silently skipped. A bare name (timestamp) and an explicit reference to the type’s own field (this.timestamp) are both resolved; a monitor reached through another object (other.timestamp) or returned by a call is not.

Rationale

A value-based class has no accessible constructor and is produced only through factory methods that make no commitment about instance identity: two calls that produce "the same" value are not guaranteed to return the same reference, and the JVM is free to treat separate instances as interchangeable. Synchronizing on such an instance gives no guarantee about who else the runtime considers to be holding the very same monitor, which opens the code up to contention it cannot reason about.

Optional<Task> pending = repository.findPending();
synchronized (pending) { // the "same" Optional may not be the same object next time
  process(pending);
}

Remediation

Use a dedicated lock object the class owns outright, never a value-based instance:

private final Object pendingLock = new Object();

Optional<Task> pending = repository.findPending();
synchronized (pendingLock) {
  process(pending);
}

© 2026 Xygeni. All rights reserved