Why is this an issue?

Java virtual threads enable the JVM to optimize the use of OS threads by mounting and unmounting them as needed. This makes them more efficient when dealing with blocking operations such as I/O or HTTP requests.

This rule applies only to code running on Java versions older than 24. Since Java 24, virtual threads are no longer pinned when executing synchronized code.

For Java version 21 to 23, when code is executed inside a synchronized block or method, the virtual thread remains pinned to its underlying OS thread and cannot be unmounted during a blocking operation. This causes the OS thread to be blocked, which can impact the scalability of the application.

Therefore, in environments running a Java version below 24, virtual threads should not execute code that contains synchronized blocks or invokes synchronized methods. Platform threads should be used in these cases instead.

This rule raises an issue when a virtual thread contains synchronized blocks or invokes synchronized methods.

Code examples

Noncompliant code example

void enqueue(){
    Thread.startVirtualThread(() -> { // Noncompliant; use a platform thread instead
            setupOperations();
            dequeLogic();
        }
    });
}

Compliant solution

void enqueue(){
    new Thread(() -> {
        synchronized {
            setupOperations();
            dequeLogic();
        }
    }).start();
}

Noncompliant code example

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

void enqueue2(){
    new Thread(() -> {
        if(someCondition){
            synchronizedMethod();
        }else{
            defaultLogic();
        }
    }).start();
}
synchronized void synchronizedMethod(){}
void defaultLogic(){}

Resources

Documentation