
Co-authored-by: Marco Borgeaud <89914223+marco-antognini-sonarsource@users.noreply.github.com>
127 lines
4.2 KiB
Plaintext
127 lines
4.2 KiB
Plaintext
== 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:
|
|
|
|
[source,java]
|
|
----
|
|
// 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
|
|
|
|
[source,java,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
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
|
|
|
|
[source,java,diff-id=1,diff-type=compliant]
|
|
----
|
|
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 - https://wiki.sei.cmu.edu/confluence/x/1zdGBQ[Do not synchronize on objects that may be reused]
|
|
* OpenJDK - https://openjdk.java.net/jeps/390[JEP 390: Warnings for Value-Based Classes]
|
|
* Java Documentation - https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/doc-files/ValueBased.html[Value-based Classes]
|
|
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
=== Message
|
|
|
|
Synchronize on a new "Object" instead.
|
|
|
|
|
|
'''
|
|
|
|
endif::env-github,rspecator-view[]
|