Equals On Atomic Value

ID

java.equals_on_atomic

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Java

Tags

concurrency, reliability

Description

Reports .equals(..) calls whose receiver is declared AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference or one of the Atomic*Array types. None of these classes override Object.equals(Object), so the call compares object identity instead of the wrapped value.

Rationale

java.util.concurrent.atomic classes are mutable holders built for lock-free updates, not value types. They inherit equals(Object) straight from Object, so it returns true only when both references point at the very same instance:

AtomicLong a = new AtomicLong(42);
AtomicLong b = new AtomicLong(42);

if (a.equals(b)) {      // Bug: always false here -- a and b are different instances
    merge(a, b);
}

The condition above is always false even though both counters hold 42, so merge never runs. Because the code compiles and equals looks like the right method to call, the mistake usually survives review and only shows up as a hard-to-reproduce behavioural bug.

Remediation

Compare the wrapped value with .get(), or compare identity explicitly with == when that is really the intent:

AtomicLong a = new AtomicLong(42);
AtomicLong b = new AtomicLong(42);

if (a.get() == b.get()) {   // Compares the wrapped value
    merge(a, b);
}

if (a == b) {                // Compares identity, and says so
    reportDuplicateReference();
}

© 2026 Xygeni. All rights reserved