org.sonar.l10n.java.rules.java.S1860.html Maven / Gradle / Ivy
Why is this an issue?
In Java, value-based classes are those for which instances are final and immutable, like String
, Integer
and so on, and
their identity relies on their value and not their reference. When a variable of one of these types is instantiated, the JVM caches its value, and the
variable is just a reference to that value. For example, multiple String
variables with the same value "Hello world!" will refer to the
same cached string literal in memory.
The synchronized
keyword tells the JVM to only allow the execution of the code contained in the following block to one
Thread
at a time. This mechanism relies on the identity of the object that is being synchronized between threads, to prevent that if
object X is locked, it will still be possible to lock another object Y.
It means that the JVM will fail to correctly synchronize threads on instances of the aforementioned value-based classes, for instance:
// These variables "a" and "b" will effectively reference the same object in memory
Integer a = 0;
Integer b = 0;
// This means that in the following code, the JVM could try to lock and execute
// on the variable "a" because "b" was notified to be released, as the two Integer variables
// are the same object to the JVM
void syncMethod(int x) {
synchronized (a) {
if (a == x) {
// ... do something here
}
}
synchronized (b) {
if (b == x) {
// ... do something else
}
}
}
This behavior can cause unrelated threads to deadlock with unclear stacktraces.
Within the JDK, types which should not be used for synchronization include:
-
String
literals
- Primitive wrapper classes in
java.lang
(such as Boolean
with Boolean.FALSE
and
Boolean.TRUE
)
- The class
java.lang.Runtime.Version
- The
Optional*
classes in java.util
: Optional
, OptionalInt
, OptionalLong
, and
OptionalDouble
- Various classes in the
java.time
API: Instant
, LocalDate
, LocalTime
,
LocalDateTime
, ZonedDateTime
, ZoneId
, OffsetTime
, OffsetDateTime
,
ZoneOffset
, Duration
, Period
, Year
, YearMonth
, and MonthDay
- Various classes in the
java.time.chrono
API: MinguoDate
, HijrahDate
, JapaneseDate
, and
ThaiBuddhistDate
- The interface
java.lang.ProcessHandle
and its implementation classes
- The implementation classes of the collection factories in
java.util
: List.of
, List.copyOf
,
Set.of
, Set.copyOf
, Map.of
, Map.copyOf
, Map.ofEntries
, and Map.entry
.
How to fix it
Replace instances of value-based classes with a new object instance to synchronize on.
Code examples
Noncompliant code example
private static final Boolean bLock = Boolean.FALSE;
private static final Integer iLock = Integer.valueOf(0);
private static final String sLock = "LOCK";
private static final List<String> listLock = List.of("a", "b", "c", "d");
public void doSomething() {
synchronized(bLock) { // Noncompliant
...
}
synchronized(iLock) { // Noncompliant
...
}
synchronized(sLock) { // Noncompliant
...
}
synchronized(listLock) { // Noncompliant
...
}
Compliant solution
private static final Object lock1 = new Object();
private static final Integer iLock = new Integer(42);
private static final String sLock = new String("A brand new string in memory!");
private static final List<String> listLock = new ArrayList<>();
public void doSomething() {
synchronized(lock1) { // Compliant
...
}
synchronized(iLock) { // Compliant
...
}
synchronized(sLock) { // Compliant
...
}
synchronized(listLock) { // Compliant
...
}
Resources
- CERT - Do not synchronize on objects that may be reused
- OpenJDK - JEP 390: Warnings for Value-Based Classes
- Java Documentation - Value-based
Classes