When using virtual threads in Java 21 there is a way to limit the number of underlying platform threads by specifying jdk.virtualThreadScheduler.parallelism
and jdk.virtualThreadScheduler.maxPoolSize
as described in this answer. If I set these variables to 1, the execution of all virtual threads will be multiplexed on a single OS thread as I understand. Do I still need to use synchronisation mechanisms like ReentrantLock
and volatile
in that case, or would it be safe to skip them altogether as long as the shared data is only accessed from the virtual threads?
When using virtual threads in Java 21 there is a way to limit the number of underlying platform threads by specifying jdk.virtualThreadScheduler.parallelism
and jdk.virtualThreadScheduler.maxPoolSize
as described in this answer. If I set these variables to 1, the execution of all virtual threads will be multiplexed on a single OS thread as I understand. Do I still need to use synchronisation mechanisms like ReentrantLock
and volatile
in that case, or would it be safe to skip them altogether as long as the shared data is only accessed from the virtual threads?
2 Answers
Reset to default 2Synchronization/locking prevents subject instructions from being interlaced. Virtual or not doesn't change that.
Remember the old days with single cpu and multiple threads guaranteed to never run instruction in parallel; we still needed synchronization in case of cpu context switching.
Do I still need to use synchronisation mechanisms like ReentrantLock and volatile in that case
In your vision of virtual threads you concentrate on Continuation, the concept of platform Carrier thread picking up ready-to-go virtual thread and continue the execution of behalf of the latter. But you seem to miss another concept, crucial to virtual threads: Context Switching. When a Carrier thread gets dismounted by one virtual thread and gets mounted on by another, all the footprints (Context) of the first one are eliminated from the Carrier and the appropriate state (Context) of second one is loaded. This includes not only locks and intrinsic synchronization monitors (volatile
, I think, is another story as it is more simple code-based insertion of memory barriers and does not represent Context state), but also set of ThreadLocal
s.
Therefore, your next statement
the shared data is only accessed from the virtual threads
is not fully correct even if you will somehow manage to limit the amount of Carrier threads to only one (which still might be marginally possible if you configure your own Executor
for virtual threads). The virtual threads, even if they are always mounted on the same Carrier, don't share more data than the traditional platform ones.
jdk.virtualThreadScheduler
options won't help you with memory consistency effects. – SpaceTrucker Commented Feb 4 at 13:09ForkJoinPool
implementation ofExecutor
. If you look at this one, you might realize that it is impossible to restrict the FJP to using only one thread. – igor.zh Commented Feb 4 at 14:25