122 lines
3.2 KiB
Plaintext
122 lines
3.2 KiB
Plaintext
![]() |
== 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]
|