VirtualThread starvation when setting special parallelism

XMLWordPrintable

    • generic
    • generic

      A DESCRIPTION OF THE PROBLEM :
        I'm using VirtualThread on my application in jdk 25.0.1 and found that sometimes a Thread.sleep() call does not return within the given time; instead, it may delay for several seconds. So I wrote a test code to reproduce this problem. And when you set a special parallelism, it seems that there will be some starvation behavior for virtual threads
        I used a debugger to delve deeper into the cause of this problem and discovered it was due to the number of work queues in the ForkJoinPool being more than twice the number of worker threads (schedulers). As is well known, the ForkJoinPool's work queues are divided into dedicated queues for worker threads and external queues. Worker threads can steal tasks from the external queues. If a thread steals a task and finds that another thread is already stealing it, it will try to switch to another external queue. Moreover, when there are tasks continuously flowing into the external queues while there are no tasks flowing into the dedicated queues, the thread will become stuck on the external queues, continuously stealing tasks. Based on the current behavior of initializing the work queue array, when we set a specific degree of parallelism, it may cause the number of external queues to exceed the number of worker threads. When the queue being stolen from continues to receive tasks, other external queues may experience task starvation because they cannot be stolen from.
        Therefore, I believe the formula for calculating the number of work queues should be adjusted, and the result should not exceed twice the parallelism, in order to avoid this potential starvation behavior.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the test case code in jdk 25.0.1(or jdk 21.0.4), you will find the starvation of Sleep100Task due to no "sleep done" log found in STDOUT.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      sleeping in Sleep100Task can wakeup as expected
      ACTUAL -
      sleeping in Sleep100Task cannot wakeup.

      ---------- BEGIN SOURCE ----------
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      import java.util.concurrent.atomic.AtomicInteger;

      public class VirtualThreadSleepStarvationReproducer {

      public static void main(String[] args) throws Exception {
      System.setProperty("jdk.virtualThreadScheduler.parallelism", "3");

      ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
      for (int i = 0; i < 512; i++) {
      executor.execute(new Sleep0Task());
      }

      executor.execute(new Sleep100Task());
      Thread.sleep(Integer.MAX_VALUE);
      }

      private static class Sleep0Task implements Runnable {
      private static final AtomicInteger threadIndex = new AtomicInteger(0);

      @Override
      public void run() {
      Thread.currentThread().setName("virtual-thread-sleep0ms-" + threadIndex.getAndIncrement());
      try {
      while (true) {
      Thread.sleep(0L);
      }
      } catch (Exception e) {
      System.out.println("[ERROR] " + Thread.currentThread().getName() + " " + e.getMessage());
      }
      }
      }

      private static class Sleep100Task implements Runnable {
      @Override
      public void run() {
      Thread.currentThread().setName("virtual-thread-sleep100ms");
      try {
      while (true) {
      long begin = System.currentTimeMillis();
      System.out.println(Thread.currentThread().getName() + " start sleep");
      Thread.sleep(100L);
      System.out.println(Thread.currentThread().getName() + " sleep done, cost = " + (System.currentTimeMillis() - begin));
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }
      }
      ---------- END SOURCE ----------

            Assignee:
            Alan Bateman
            Reporter:
            Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: