ScheduledExecutorService consumes 100% CPU when corePoolSize = 0












3














I've ran into an interesting issue in production.



I've had the following ScheduledThreadPool allocation code:



ScheduledExecutorService executorService =
Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() - 1);


The thread pool was processing some tasks from the queue periodically. And everything was working fine till the moment when the service was deployed on a single core environment. Apparently, the line above converted to:



ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);


Since then, the JVM process CPU utilization was constantly around 100%. The moment I've changed Runtime.getRuntime().availableProcessors() - 1 to constant 1 the issue have gone.



It took sometime to find out the root cause, but still I don't know the reason behind it. ScheduledExecutorService JavaDoc states:



/**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
* @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}


Basically, 0 (zero) is a valid argument for the thread pool instantiation, but it works super weird with this value.



Can please someone explain why so?



Simple and verifiable test case



import java.util.Queue;
import java.util.concurrent.*;

public class Test {

public static void main(String args) throws InterruptedException {
MessageTaskExecutor asyncEmailGatewayTaskExecutor = new MessageTaskExecutor();

// Infinitely add new tasks to the queue every second
for (int i = 1; ; i++) {
System.out.println(String.format("Adding message #%s to the queue", i));

asyncEmailGatewayTaskExecutor.putMessageIntoQueue(i);

Thread.sleep(1_000);
}
}

static class MessageTaskExecutor {

static final int INITIAL_DELAY_SECONDS = 1;
static final int PROCESSING_RATE_MILLISECONDS = 5_000;

final Queue<Runnable> messageQueue = new ArrayBlockingQueue<>(1_000_000);
final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);

MessageTaskExecutor() {
// Scavenging Message Tasks Queue every 'PROCESSING_RATE_MILLISECONDS'. Initial delay is fixed for 'INITIAL_DELAY_SECONDS'
executorService.schedule(this::processEmailTasks, INITIAL_DELAY_SECONDS, TimeUnit.SECONDS);
}

void putMessageIntoQueue(int messageId) {
Runnable messageTask = () -> System.out.println(String.format("Message #%s is getting processed!", messageId));

messageQueue.offer(messageTask);
}

void processEmailTasks() {
System.out.println(String.format("There are %s messages in the queue. Processing the messages...", messageQueue.size()));

// Processing messages queue
while (!messageQueue.isEmpty()) {
executorService.submit(messageQueue.poll()); // Submitting task to executor service
}

// Re-scheduling processing job
executorService.schedule(this::processEmailTasks, PROCESSING_RATE_MILLISECONDS, TimeUnit.MILLISECONDS);
}
}
}


This code allocates ~30 MB and JVM process consumes ~100% CPU on a single-core virtual machine (tested on Win 7/CentOS 7). JDK 1.8.0.181.



By changing field:



final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);


to:



final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);


CPU consumption goes down to normal 3-5%.










share|improve this question



























    3














    I've ran into an interesting issue in production.



    I've had the following ScheduledThreadPool allocation code:



    ScheduledExecutorService executorService =
    Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() - 1);


    The thread pool was processing some tasks from the queue periodically. And everything was working fine till the moment when the service was deployed on a single core environment. Apparently, the line above converted to:



    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);


    Since then, the JVM process CPU utilization was constantly around 100%. The moment I've changed Runtime.getRuntime().availableProcessors() - 1 to constant 1 the issue have gone.



    It took sometime to find out the root cause, but still I don't know the reason behind it. ScheduledExecutorService JavaDoc states:



    /**
    * Creates a thread pool that can schedule commands to run after a
    * given delay, or to execute periodically.
    * @param corePoolSize the number of threads to keep in the pool,
    * even if they are idle
    * @return a newly created scheduled thread pool
    * @throws IllegalArgumentException if {@code corePoolSize < 0}
    */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
    }


    Basically, 0 (zero) is a valid argument for the thread pool instantiation, but it works super weird with this value.



    Can please someone explain why so?



    Simple and verifiable test case



    import java.util.Queue;
    import java.util.concurrent.*;

    public class Test {

    public static void main(String args) throws InterruptedException {
    MessageTaskExecutor asyncEmailGatewayTaskExecutor = new MessageTaskExecutor();

    // Infinitely add new tasks to the queue every second
    for (int i = 1; ; i++) {
    System.out.println(String.format("Adding message #%s to the queue", i));

    asyncEmailGatewayTaskExecutor.putMessageIntoQueue(i);

    Thread.sleep(1_000);
    }
    }

    static class MessageTaskExecutor {

    static final int INITIAL_DELAY_SECONDS = 1;
    static final int PROCESSING_RATE_MILLISECONDS = 5_000;

    final Queue<Runnable> messageQueue = new ArrayBlockingQueue<>(1_000_000);
    final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);

    MessageTaskExecutor() {
    // Scavenging Message Tasks Queue every 'PROCESSING_RATE_MILLISECONDS'. Initial delay is fixed for 'INITIAL_DELAY_SECONDS'
    executorService.schedule(this::processEmailTasks, INITIAL_DELAY_SECONDS, TimeUnit.SECONDS);
    }

    void putMessageIntoQueue(int messageId) {
    Runnable messageTask = () -> System.out.println(String.format("Message #%s is getting processed!", messageId));

    messageQueue.offer(messageTask);
    }

    void processEmailTasks() {
    System.out.println(String.format("There are %s messages in the queue. Processing the messages...", messageQueue.size()));

    // Processing messages queue
    while (!messageQueue.isEmpty()) {
    executorService.submit(messageQueue.poll()); // Submitting task to executor service
    }

    // Re-scheduling processing job
    executorService.schedule(this::processEmailTasks, PROCESSING_RATE_MILLISECONDS, TimeUnit.MILLISECONDS);
    }
    }
    }


    This code allocates ~30 MB and JVM process consumes ~100% CPU on a single-core virtual machine (tested on Win 7/CentOS 7). JDK 1.8.0.181.



    By changing field:



    final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);


    to:



    final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);


    CPU consumption goes down to normal 3-5%.










    share|improve this question

























      3












      3








      3


      1





      I've ran into an interesting issue in production.



      I've had the following ScheduledThreadPool allocation code:



      ScheduledExecutorService executorService =
      Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() - 1);


      The thread pool was processing some tasks from the queue periodically. And everything was working fine till the moment when the service was deployed on a single core environment. Apparently, the line above converted to:



      ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);


      Since then, the JVM process CPU utilization was constantly around 100%. The moment I've changed Runtime.getRuntime().availableProcessors() - 1 to constant 1 the issue have gone.



      It took sometime to find out the root cause, but still I don't know the reason behind it. ScheduledExecutorService JavaDoc states:



      /**
      * Creates a thread pool that can schedule commands to run after a
      * given delay, or to execute periodically.
      * @param corePoolSize the number of threads to keep in the pool,
      * even if they are idle
      * @return a newly created scheduled thread pool
      * @throws IllegalArgumentException if {@code corePoolSize < 0}
      */
      public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
      return new ScheduledThreadPoolExecutor(corePoolSize);
      }


      Basically, 0 (zero) is a valid argument for the thread pool instantiation, but it works super weird with this value.



      Can please someone explain why so?



      Simple and verifiable test case



      import java.util.Queue;
      import java.util.concurrent.*;

      public class Test {

      public static void main(String args) throws InterruptedException {
      MessageTaskExecutor asyncEmailGatewayTaskExecutor = new MessageTaskExecutor();

      // Infinitely add new tasks to the queue every second
      for (int i = 1; ; i++) {
      System.out.println(String.format("Adding message #%s to the queue", i));

      asyncEmailGatewayTaskExecutor.putMessageIntoQueue(i);

      Thread.sleep(1_000);
      }
      }

      static class MessageTaskExecutor {

      static final int INITIAL_DELAY_SECONDS = 1;
      static final int PROCESSING_RATE_MILLISECONDS = 5_000;

      final Queue<Runnable> messageQueue = new ArrayBlockingQueue<>(1_000_000);
      final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);

      MessageTaskExecutor() {
      // Scavenging Message Tasks Queue every 'PROCESSING_RATE_MILLISECONDS'. Initial delay is fixed for 'INITIAL_DELAY_SECONDS'
      executorService.schedule(this::processEmailTasks, INITIAL_DELAY_SECONDS, TimeUnit.SECONDS);
      }

      void putMessageIntoQueue(int messageId) {
      Runnable messageTask = () -> System.out.println(String.format("Message #%s is getting processed!", messageId));

      messageQueue.offer(messageTask);
      }

      void processEmailTasks() {
      System.out.println(String.format("There are %s messages in the queue. Processing the messages...", messageQueue.size()));

      // Processing messages queue
      while (!messageQueue.isEmpty()) {
      executorService.submit(messageQueue.poll()); // Submitting task to executor service
      }

      // Re-scheduling processing job
      executorService.schedule(this::processEmailTasks, PROCESSING_RATE_MILLISECONDS, TimeUnit.MILLISECONDS);
      }
      }
      }


      This code allocates ~30 MB and JVM process consumes ~100% CPU on a single-core virtual machine (tested on Win 7/CentOS 7). JDK 1.8.0.181.



      By changing field:



      final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);


      to:



      final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);


      CPU consumption goes down to normal 3-5%.










      share|improve this question













      I've ran into an interesting issue in production.



      I've had the following ScheduledThreadPool allocation code:



      ScheduledExecutorService executorService =
      Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() - 1);


      The thread pool was processing some tasks from the queue periodically. And everything was working fine till the moment when the service was deployed on a single core environment. Apparently, the line above converted to:



      ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);


      Since then, the JVM process CPU utilization was constantly around 100%. The moment I've changed Runtime.getRuntime().availableProcessors() - 1 to constant 1 the issue have gone.



      It took sometime to find out the root cause, but still I don't know the reason behind it. ScheduledExecutorService JavaDoc states:



      /**
      * Creates a thread pool that can schedule commands to run after a
      * given delay, or to execute periodically.
      * @param corePoolSize the number of threads to keep in the pool,
      * even if they are idle
      * @return a newly created scheduled thread pool
      * @throws IllegalArgumentException if {@code corePoolSize < 0}
      */
      public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
      return new ScheduledThreadPoolExecutor(corePoolSize);
      }


      Basically, 0 (zero) is a valid argument for the thread pool instantiation, but it works super weird with this value.



      Can please someone explain why so?



      Simple and verifiable test case



      import java.util.Queue;
      import java.util.concurrent.*;

      public class Test {

      public static void main(String args) throws InterruptedException {
      MessageTaskExecutor asyncEmailGatewayTaskExecutor = new MessageTaskExecutor();

      // Infinitely add new tasks to the queue every second
      for (int i = 1; ; i++) {
      System.out.println(String.format("Adding message #%s to the queue", i));

      asyncEmailGatewayTaskExecutor.putMessageIntoQueue(i);

      Thread.sleep(1_000);
      }
      }

      static class MessageTaskExecutor {

      static final int INITIAL_DELAY_SECONDS = 1;
      static final int PROCESSING_RATE_MILLISECONDS = 5_000;

      final Queue<Runnable> messageQueue = new ArrayBlockingQueue<>(1_000_000);
      final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);

      MessageTaskExecutor() {
      // Scavenging Message Tasks Queue every 'PROCESSING_RATE_MILLISECONDS'. Initial delay is fixed for 'INITIAL_DELAY_SECONDS'
      executorService.schedule(this::processEmailTasks, INITIAL_DELAY_SECONDS, TimeUnit.SECONDS);
      }

      void putMessageIntoQueue(int messageId) {
      Runnable messageTask = () -> System.out.println(String.format("Message #%s is getting processed!", messageId));

      messageQueue.offer(messageTask);
      }

      void processEmailTasks() {
      System.out.println(String.format("There are %s messages in the queue. Processing the messages...", messageQueue.size()));

      // Processing messages queue
      while (!messageQueue.isEmpty()) {
      executorService.submit(messageQueue.poll()); // Submitting task to executor service
      }

      // Re-scheduling processing job
      executorService.schedule(this::processEmailTasks, PROCESSING_RATE_MILLISECONDS, TimeUnit.MILLISECONDS);
      }
      }
      }


      This code allocates ~30 MB and JVM process consumes ~100% CPU on a single-core virtual machine (tested on Win 7/CentOS 7). JDK 1.8.0.181.



      By changing field:



      final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);


      to:



      final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);


      CPU consumption goes down to normal 3-5%.







      java performance threadpool threadpoolexecutor






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 20 '18 at 20:42









      Mikhail Kholodkov

      4,13952546




      4,13952546
























          1 Answer
          1






          active

          oldest

          votes


















          5














          This is a known bug: JDK-8129861. It has been fixed in JDK 9.



          The workaround is to set core pool size at least 1:



          int corePoolSize = Math.max(Runtime.getRuntime().availableProcessors() - 1, 1);





          share|improve this answer





















            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53401197%2fscheduledexecutorservice-consumes-100-cpu-when-corepoolsize-0%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            5














            This is a known bug: JDK-8129861. It has been fixed in JDK 9.



            The workaround is to set core pool size at least 1:



            int corePoolSize = Math.max(Runtime.getRuntime().availableProcessors() - 1, 1);





            share|improve this answer


























              5














              This is a known bug: JDK-8129861. It has been fixed in JDK 9.



              The workaround is to set core pool size at least 1:



              int corePoolSize = Math.max(Runtime.getRuntime().availableProcessors() - 1, 1);





              share|improve this answer
























                5












                5








                5






                This is a known bug: JDK-8129861. It has been fixed in JDK 9.



                The workaround is to set core pool size at least 1:



                int corePoolSize = Math.max(Runtime.getRuntime().availableProcessors() - 1, 1);





                share|improve this answer












                This is a known bug: JDK-8129861. It has been fixed in JDK 9.



                The workaround is to set core pool size at least 1:



                int corePoolSize = Math.max(Runtime.getRuntime().availableProcessors() - 1, 1);






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 20 '18 at 20:53









                apangin

                48.9k795124




                48.9k795124






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53401197%2fscheduledexecutorservice-consumes-100-cpu-when-corepoolsize-0%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Create new schema in PostgreSQL using DBeaver

                    Deepest pit of an array with Javascript: test on Codility

                    Fotorealismo