In order to maintain the performance in a distributed system, we might require to limit the number of threads in a thread pool. We have two approaches:
Executor Service & Thread Pools .
I feel executor service is the easier approach whereas Thread Pools is required when we need customization.
Executor Service:
Executor Service is the child interface of Executor. Basically, we can create the following types if executor service as:
FixedThreadPool : To create a fixed number of threads, if more tasks are submitted then the idle threads in the thread pools the tasks will wait.
ScheduledThreadPool
CachedThreadPool : Threads will execute the task and are killed.
SingleThreadPool : Only one thread in the entire thread pool.
We just require to create an object of the Runnable/Thread type and submit it in the threadpool.
A sample code snippet is as follows:
ExecutorService executor= Executors.newScheduledThreadPool(5);
for(int i=0;i<10;i++)
{
Runnable worker=new WorkerThread(i);
executor.execute(worker);
}
executor.shutdown();
if (executor.isTerminated()) {
System.out.println(“Completed..”);
}
}
class WorkerThread implements Runnable
{
private int i;
public WorkerThread(int i) {
this.i=i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+” Start= “+i);
System.out.println(Thread.currentThread().getName()+” End.”);
}
}
ThreadPoolExecutor:
Thread Pool Executor is perhaps a better approach where we have a lot of options.
ExecutorService threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize,keepAliveTime,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory,rejectionHandler);
Let us see what the above parameters mean:
Core and maximum pool sizes :
When a new task is submitted in method , and fewer than corePoolSize threads are running, a new thread is created to handle the request, even if other worker threads are idle.
If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full.
Keep-alive times :
If the pool currently has more than corePbeoolSize threads, excess threads will be terminated if they have been idle for more than the keepAliveTime.
Queuing:
Any blocking queue can be used to transfer and hold the submitted task such as a Synchronous Queue, a LinkedBlockingQueue or an ArrayBlockingQueue.
Thread Factory: A default thread factory can be used as
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler : Implementation to handle the jobs that can’t fit in the worker queue.
For that we need to create a class which implements the RejectedExecutionHandler.
class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
{
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(r.toString()+” is rejected”);
}
}
So we create the executor pool as:
ThreadPoolExecutor executorPool=new ThreadPoolExecutor(2, 4, 10,TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(2),threadFactory,rejectionHandler);
And then we add a new task in the thread pool as
executorPool.execute(new WorkerThread(3));
After all the tasks has been executed by the thread pool we should terminate the thread pool as executorPool.shutdown();