rspec/rules/S6906/java/rule.adoc

122 lines
3.2 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
Java 21 virtual threads allow the JVM to optimize the usage of OS threads,
by mounting and unmounting them on an OS thread when needed, and
making them extremely efficient when dealing with blocking operations such as HTTP requests or I/O.
However, there are cases in which a virtual thread cannot be unmounted during blocking operations because it is pinned to the underlying OS thread:
* When it executes code inside a synchronized block or method
* When it executes a native method
In these scenarios, the virtual thread will not be unmounted and the blocking operation will cause the OS thread to be blocked.
A pinned virtual thread does not necessarily mean that the application is incorrect, but it can hinder its scalability.
Therefore, virtual threads should not be used when their task includes synchronized code or native methods.
Instead, the default platform threads should be used.
This rule raises an issue when the task passed to a virtual thread includes synchronized code or native methods.
=== Code examples
==== Noncompliant code example
[source,java,diff-id=1,diff-type=noncompliant]
----
void enqueue(){
Thread.startVirtualThread(() -> { // Noncompliant; use a platform thread instead of a virtual one
synchronized {
setupOperations();
dequeLogic();
}
});
}
----
==== Compliant solution
[source,java,diff-id=1,diff-type=compliant]
----
void enqueue(){
new Thread(() -> {
synchronized {
setupOperations();
dequeLogic();
}
}).start();
}
----
==== Noncompliant code example
[source,java,diff-id=2,diff-type=noncompliant]
----
void enqueue2(){
Thread.startVirtualThread(() -> { // Noncompliant; use a platform thread instead of a virtual one
if(someCondition){
synchronizedMethod();
}else{
defaultLogic();
}
});
}
synchronized void synchronizedMethod(){}
void defaultLogic(){}
----
==== Compliant solution
[source,java,diff-id=2,diff-type=compliant]
----
void enqueue2(){
new Thread(() -> {
if(someCondition){
synchronizedMethod();
}else{
defaultLogic();
}
}).start();
}
synchronized void synchronizedMethod(){}
void defaultLogic(){}
----
==== Noncompliant code example
[source,java,diff-id=3,diff-type=noncompliant]
----
public class NativeExample {
public native void printHelloWorld();
static {
System.loadLibrary("nativeexample");
}
public static void main(String[] args) {
Thread.startVirtualThread(() -> { // Noncompliant; use a platform thread instead of a virtual one
new NativeExample().printHelloWorld();
});
}
}
----
==== Compliant solution
[source,java,diff-id=3,diff-type=compliant]
----
public class NativeExample {
public native void printHelloWorld();
static {
System.loadLibrary("nativeexample");
}
public static void main(String[] args) {
new Thread(() -> {
new NativeExample().printHelloWorld();
}).start();
}
}
----
== Resources
=== Documentation
* Java Documentation - https://openjdk.org/jeps/444#:~:text=There%20are%20two,by%20capturing%20carriers[Virtual threads, pinning scenarios]