Search code examples
javaconcurrencyjava-21virtual-threads

Why ReentrantLock is better for virtual threads than synchronized?


I've visited some IT conference and on a session about virtual threads feature in Java 21+ was mentioned that synchronized might(!) be a problem for virtual threads because of pinning virtual thread and if it is a problem - usage of Lock could solve the problem.

I don't understand why Lock could be better than syncronized in context of virtual threads. I suppose that ReentrantLock could be a cause of the same pinning. isn't it ? If not - why ?


Solution

  • Virtual threads (fibers) are indicated for tasks that involve blocking, tasks that are not CPU-bound. Blocking tasks include those that perform file I/O, logging, network calls, database access, etc.

    When a virtual thread is blocked, it can be swiftly switched out for another virtual thread to keep the CPU core busy. This quick switching between virtual threads is what makes them so highly performant.

    The current implementation in Java 21 and Java 22 has a limitation. While code inside a synchronized block is running, Java cannot detect if that code is blocked or not. That task cannot be switched out for another virtual thread to execute. So that blocked code sits there doing nothing but waiting, and therefore that core sits there not doing any useful work.

    This problem of a stuck virtual thread is called pinning. The blocked virtual thread is pinned to the host platform thread on that core. Pinning defeats the purpose of using virtual threads.

    Brief pinning is not a problem. For example, a quick check of a protected guard variable. But a long-running chunk of pinned code is a problem. Such a long-running chunk of synchronized code should either (a) not be run on a virtual thread, or (b) should be rewritten to use a ReentrantLock rather than use synchronized.

    The Project Loom team continues to examine this issue. A solution is in the works. So a future version of Java may be free of this limitation.

    By the way, a similar pinning situation is native code. Java code that uses JNI or Foreign Functions to call long-running native code should not be used in a virtual thread.

    To understand virtual threads:

    • Study JEP 444.
    • See video presentations by Ron Pressler, Alan Bateman, and José Paumard.