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

ScheduledThreadPoolExecutor.allowCoreThreadTimeOut breaks task scheduling

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      Actual OS is Windows 11, With Eclipse Temurin as the OpenJDK provider. Specific JDK version:
      openjdk 21.0.6 2025-01-21 LTS
      OpenJDK Runtime Environment Temurin-21.0.6+7 (build 21.0.6+7-LTS)
      OpenJDK 64-Bit Server VM Temurin-21.0.6+7 (build 21.0.6+7-LTS, mixed mode, sharing)

      A DESCRIPTION OF THE PROBLEM :
      The issue arose while i was looking into a thread-per-task executor with scheduling. To allow (in my case virtual) threads to still die after having been used for one-off tasks, i set allowCoreThreadTimeOut to true.

      Note that the core pool size is set to Integer.MAX_VALUE, so that should not be a limiting factor. Although according to the javadoc, even setting it to 0 should have no adverse affects on scheduled tasks, since it is not supposed to be an upper bound (although in reality it is, but that is either an error in docs or another bug).

      This causes some very weird behavior with scheduled tasks, easiest to see when running th sample code a couple of times with the two marked sections. Using a debugger keep an eye on the threads which are opened and running at any given time.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the test code without changing anything, then without allowCoreThreadTimeOut, then with allowCoreThreadTimeOut and the two submits. The sleep in the submits are important.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      In any of these scenarios, the scheduled tasks should be executed on time, creating a new thread for the task if need be.
      ACTUAL -
      The threads are executed using a single thread if allowCoreThreadTimeOut is true.

      They will use more than one thread again if tasks are added via submit() beforehand. That will delay them by the time it takes to execute the submits though.

      ---------- BEGIN SOURCE ----------
      import java.time.Instant;
      import java.util.concurrent.Callable;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      import java.util.concurrent.Future;
      import java.util.concurrent.ScheduledExecutorService;
      import java.util.concurrent.ScheduledThreadPoolExecutor;
      import java.util.concurrent.ThreadFactory;
      import java.util.concurrent.TimeUnit;

      public class Testy {
      public static void main(String[] args) throws InterruptedException {
      ScheduledThreadPoolExecutor scheduledExecutorService = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(Integer.MAX_VALUE);

      // comment out below line to restore expected behavior
      scheduledExecutorService.allowCoreThreadTimeOut(true);

      Callable<String> scheduledCallable1 = () -> {
      Thread.sleep(5000);
      System.out.println(Instant.now() + " T1 done");
      return "Done";
      };
      Callable<String> scheduledCallable2 = () -> {
      Thread.sleep(5000);
      System.out.println(Instant.now() + " T2 done");
      return "Done";
      };
      Runnable scheduledRunnable = () -> {
      try {
      Thread.sleep(5000);
      } catch (InterruptedException e) {}
      System.out.println(Instant.now() + " R done");
      };

      // comment in below two lines to "fix" behavior
      Future<String> future1 = scheduledExecutorService.submit(() -> {
      // will not work without this sleep to cause it to overlap with the schedule time of scheduledCallable1/2
      Thread.sleep(2000);
      System.out.println(Instant.now() + " directly submitted 1");
      return "Done";});
      Future<String> future2 = scheduledExecutorService.submit(() -> {
      Thread.sleep(2000);
      System.out.println(Instant.now() + " directly submitted 2");
      return "Done";});

      scheduledExecutorService.schedule(scheduledCallable1, 1, TimeUnit.SECONDS);
      scheduledExecutorService.schedule(scheduledCallable2, 1, TimeUnit.SECONDS);
      scheduledExecutorService.scheduleAtFixedRate(scheduledRunnable, 1, 1, TimeUnit.SECONDS);

      while(true) {
      Thread.sleep(10000);
      System.out.println(Instant.now() + " slept 10000");
      }
      }
      }
      ---------- END SOURCE ----------

            vklang Viktor Klang
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: