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);
}