Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8305069

Starvation/deadlock due to pinning when using both synchronized and ReentrantLock

    XMLWordPrintable

Details

    • generic
    • generic

    Description

      ADDITIONAL SYSTEM INFORMATION :
      java version "20" 2023-03-21
      Java(TM) SE Runtime Environment (build 20+36-2344)
      Java HotSpot(TM) 64-Bit Server VM (build 20+36-2344, mixed mode, sharing)

      A DESCRIPTION OF THE PROBLEM :
      Virtual threads are scheduled by an ForkJoinPool which provides carrier threads. If the number of carrier threads has reached the parallelism of the ForkJoinPool, submitting tasks will not create any more carrier thread.
      And also using synchronized on some object will pin the carrier thread.
      So in the situation:
      1. all the carrier threads are pinned by synchronized on some object, but that object's monitor is owned by some virtual thread
      2. That virtual thread has owned object's monitor but there is no carrier thread for it to run

      So this could cause the deadlock. And the deadlock is not happening if change the virtual thread pool the normal thread pool

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      run the source code below

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      expect this message in sysout out:

      Main thread lock
      Main thread unlock
      First thread lock
      First thread unlock
      Second thread lock
      Second thread unlock
      Suppose to be here!!
      ACTUAL -
      actual message in sysout out:

      Main thread lock
      Main thread unlock

      ---------- BEGIN SOURCE ----------
      import java.util.concurrent.CountDownLatch;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      import java.util.concurrent.locks.ReentrantLock;

      public class VirtualThreadDeadlock {

      public static void main(String[] args) throws Exception {
      // use jdk.virtualThreadScheduler.parallelism=1 to reproduce the issue easily
      System.setProperty("jdk.virtualThreadScheduler.parallelism", "1");

      ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

      CountDownLatch latch = new CountDownLatch(2);

      ReentrantLock lock = new ReentrantLock();

      Object objLock = new Object();

      lock.lock(); // main thread locked
      System.out.println("Main thread lock");

      executor.execute(() -> {
      lock.lock(); // try to lock, continuation will yield
      try {
      System.out.println("First thread lock");
      } finally {
      lock.unlock();
      System.out.println("First thread unlock");
      }

      latch.countDown(); // first thread end
      });

      Thread.sleep(1000);

      executor.execute(() -> {
      synchronized (objLock) {
      lock.lock(); // carrierThread will be pinned
      try {
      System.out.println("Second thread lock");
      } finally {
      lock.unlock();
      System.out.println("Second thread unlock");
      }
      }

      latch.countDown(); // second thread end
      });

      Thread.sleep(1000);

      lock.unlock(); // main thread unlocked, the first thread is supposed to continue,
      // but there is no available carrierThread to execute

      System.out.println("Main thread unlock");

      latch.await();

      System.out.println("Suppose to be here!!");
      }

      }

      ---------- END SOURCE ----------

      FREQUENCY : always


      Attachments

        Activity

          People

            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated: