Difference between CachedThreadPool vs FixedThreadPool visualized in JVisualmVM

General information:

public ThreadPoolExecutor(int corePoolSize, // number of threads to keep in pool even if idle
                 int maximumPoolSize, // max number of threads to allow in the pool
                 long keepAliveTime, // max time for idle thread to wait for new task before terminating
                 TimeUnit unit, // related to keepAliveTime
                 BlockingQueue<Runnable> workQueue) {

Note that a thread pool will shrink to corePoolSize by removing threads that are idle waiting for tasks longer than keepAliveTime (if keepAliveTime > 0). If maxPoolSize is very big then a new thread will be created for each new task unless there is already an idle thread in the pool to be reused for processing.

1. FixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(
               nThreads, // once created a thread do not shrink the pool 
               nThreads,
               0L, TimeUnit.MILLISECONDS, // idle threads waits forever for new tasks
               new LinkedBlockingQueue<Runnable>());
}

example:

package com.bawi.executors;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyFixedThreadPool {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyFixedThreadPool.class);
    public static void main(String[] args) {
        sleepSeconds(10); // give 10 seconds to connect to JVisualMV

        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
        newFixedThreadPool.submit(() -> sleepSeconds(5)); // starts only first thread
        sleepSeconds(6); // wait 6s so first thread finishes running the task
        newFixedThreadPool.submit(() -> sleepSeconds(5)); // starts 2nd thread to process 2nd task even
                                                      // though 1st thread already finished processing 
                                                      // 1st task and was available/waiting
        sleepSeconds(1); // wait 1s to add 3rd task
        newFixedThreadPool.submit(() -> sleepSeconds(5)); // 3rd task is picked by the 1st thread 
                                                          // (1st thread was awailable/waiting)
        sleepSeconds(1); // wait 1s to add 4rd task
        newFixedThreadPool.submit(() -> sleepSeconds(5)); // need to wait 3s for available 
                                                          // thread before processing 4rd task 

        sleepSeconds(80); // wait until all threads finish processing tasks + additional 60 seconds
    }

    private static void sleepSeconds(int seconds) {
        try {
            LOGGER.debug(currentThreadId() + "About to sleep {} seconds", seconds);
            TimeUnit.SECONDS.sleep(seconds);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private static String currentThreadId() {
        long id = Thread.currentThread().getId();
        return String.format("Thread id=%-2s|", id);
    }
}

Output:
2015-11-05 18:05:04,289|main |Thread id=1 |About to sleep 10 seconds
2015-11-05 18:05:14,296|main |Thread id=1 |About to sleep 6 seconds
2015-11-05 18:05:14,296|pool-1-thread-1 |Thread id=16|About to sleep 5 seconds
2015-11-05 18:05:20,296|main |Thread id=1 |About to sleep 1 seconds
2015-11-05 18:05:20,296|pool-1-thread-2 |Thread id=17|About to sleep 5 seconds
2015-11-05 18:05:21,311|main |Thread id=1 |About to sleep 1 seconds
2015-11-05 18:05:21,311|pool-1-thread-1 |Thread id=16|About to sleep 5 seconds
2015-11-05 18:05:22,315|main |Thread id=1 |About to sleep 80 seconds
2015-11-05 18:05:25,296|pool-1-thread-2 |Thread id=17|About to sleep 5 seconds

Note that only two threads (pool-1-thread-1,id=16 and pool-1-thread-2, id=17) were involved in running 4 tasks. Notice on the JVisualVM that the pool did not shrink to 1 or 0 and both of the pool threads remained available/waiting (parked) for more that 60 seconds and even after main thread finished. Main thread finished in 80 seconds after submitting last task.

fixed-thread-pool

2. CachedThreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, // try to reduce the pool size to 0 if threads are idle long enough
                            Integer.MAX_VALUE, // almost no limit for the number of new threads started
                            60L, TimeUnit.SECONDS, // terminate thread after 60s idle
                            new SynchronousQueue<Runnable>());
}

example:

public class MyCashedThreadPool {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyCashedThreadPool.class);

    public static void main(String[] args) {
        sleepSeconds(10); // give 10 seconds to connect to JVisualMV

        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        newCachedThreadPool.submit(() -> sleepSeconds(5)); // starts only first thread
        sleepSeconds(6); // wait 6s so first thread finishes running the task
        newCachedThreadPool.submit(() -> sleepSeconds(5)); // reuses 1nd thread as
                                                           // available/waiting for 2nd task
        sleepSeconds(1); // wait 1s to add 3rd task
        newCachedThreadPool.submit(() -> sleepSeconds(5)); // starts 2nd thread for 3rd task 
                                                           // as 1st thread still busy
        sleepSeconds(1); // wait 1s to add 4rd task
        newCachedThreadPool.submit(() -> sleepSeconds(5)); // starts 3rd thread for 4rd task 
                                                           // as 1st and 2nd thread still busy 
        sleepSeconds(70); // wait 70s to so 1st, 2nd and 3rd thread terminate after 60s of being idle 
        newCachedThreadPool.submit(() -> sleepSeconds(5)); // starts 4th thread for 5th task 
                                                           // as no previous threads were available
        sleepSeconds(10);
    }

    private static void sleepSeconds(int seconds) {
        try {
            LOGGER.debug(currentThreadId() + "About to sleep {} seconds", seconds);
            TimeUnit.SECONDS.sleep(seconds);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String currentThreadId() {
        long id = Thread.currentThread().getId();
        return String.format("Thread id=%-2s|", id);
    }
}

Output:
2015-11-05 21:40:19,675|main |Thread id=1 |About to sleep 10 seconds
2015-11-05 21:40:29,688|main |Thread id=1 |About to sleep 6 seconds
2015-11-05 21:40:29,688|pool-1-thread-1 |Thread id=16|About to sleep 5 seconds
2015-11-05 21:40:35,701|main |Thread id=1 |About to sleep 1 seconds
2015-11-05 21:40:35,701|pool-1-thread-1 |Thread id=16|About to sleep 5 seconds
2015-11-05 21:40:36,702|main |Thread id=1 |About to sleep 1 seconds
2015-11-05 21:40:36,702|pool-1-thread-2 |Thread id=17|About to sleep 5 seconds
2015-11-05 21:40:37,705|main |Thread id=1 |About to sleep 70 seconds
2015-11-05 21:40:37,705|pool-1-thread-3 |Thread id=18|About to sleep 5 seconds
2015-11-05 21:41:47,710|main |Thread id=1 |About to sleep 10 seconds
2015-11-05 21:41:47,710|pool-1-thread-4 |Thread id=19|About to sleep 5 seconds

Note that min or max threads in the pool is not defined assuming 0 and Integer.MAX_VALUE. When a thread is available then it will be reused for a task. If thread is not available a new thread will be created in order to process a task. If a thread remains waiting (idle) for 60 seconds then it will be terminated. That could be seen on the JVisualVM where the thread pool shrinks to 0 at a certain moment and later expands again on new task.

cached-thread-pool

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s