Have you ever done the following:
new Thread(runnable).start();
all over the place, all over the code? What if there was a centralised pool for all sorts of threads to live in? This way we could achieve a certain level of thread reuse and do other smart stuff like pre-initialisation etc.
Enter Executors. As part of the java.util.concurrent package comes this amazing utility class with static factory methods as convenience methods to offer different flavours of ThreadPoolExecutor.
FixedThreadPool
We can create a FixedThreadPool as follows:
final Executor executor = Executors.newFixedThreadPool(5);
behind the scenes the static factory method is
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
This basically creates a thread pool of core size 5 and max pool size of 5. Also the pooled threads are not released (0 minutes as the third and fourth argument) and are maintained, even in idle state, for all the lifetime of the FixedThreadPool Executor. Lastly, a LinkedBlockingQueue of Runnables is provided to keep hold of the tasks and submit to worker threads.
If we now execute some runnable like this:
executor.execute(new Runnable(){public void run(){/*do work here*/}});
say for instance passing 10 runnables then 5 of them will be submitted as pooled threads and all the remaining ones would be cached in the queue. From that point onwards the 5 queued runnables will be passed one by one to the running 5 pool threads. Interestingly, if there are no more runnables for the 5 created threads, then these threads remain in idle state inside of the FixedThreadPool patiently waiting for new runnables until the fixed thread pool executor is shut down.
If we wish not to lose time we can initialise the thread pool and create our core max pool of threads before the first runnables to be submitted:
int count = ((ThreadPoolExecutor) executor).prestartAllCoreThreads();
SingleThreadExecutor
We can create a SingleThreadExecutor instance by calling the static factory method:
final Executor executor = Executors.newSingleThreadExecutor();
which behind the scenes calls the ThreadPoolExecutor constructor in the following flavour:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
Based on these arguments core pool size = max pool size = 1 and this thread is kept even as idle in the pool until the end of the single thread executor. Similarly as the FixedThreadPoolExecutor there is a LinkedBlockingQueue of Runnables to hold on the tasks before submitting them one by one on to the sole running thread.
CachedThreadPool
A CachedThreadPool Executor can be created as follows:
final Executor executor = Executors.newCachedThreadPool();
which makes a call to the ThreadPoolExecutor constructor with the following arguments:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
So although the static factory method has no arguments the ThreadPoolExecutor constructor which is called underneath creates an unbounded ThreadPoolExecutor where all runnables are making it into the executor and after successful completion they stay in idle state in the thread pool for 60 seconds before they get decommissioned if not getting used earlier.