Every concept is very big,troublesome and non-understandable if the developer thinks in mind that the concept is tough enough. Many of the java developers would be thinking same about
ThreadPool and Executor Framework that it is a very tough topic in concurrency. Believe me , it is the easiest topic I have ever learned. Just follow me to prove that.
First of all let us see the
Basics of a Thread : In any multi-threading application, if the developer is using the Thread is by following the below mentioned procedure:-
1) Create a Thread using Runnable or Thread class.
2) Call the start() method over the threads to execute the above created Thread.
Hence, the developer have to take care of creating thread and calling it explicitly. What if the developer doesn't need to create the Thread everytime he needs it? What if the developer doesn't need to call the thread everytime he needs it? That's what ThreadPool does.
ThreadPool is nothing but a good procedure to follow to separate this task of Thread Creation and Execution.
It means we don't need to worry about Thread creation . ThreadPool will handle this task . How ? will see in some moments.
Now,
Basic Idea Behind Using ThreadPool:
Let's take an example before jumping in which will illustrate the difficulties if we don't use ThreadPool. Suppose we create Threads in our Multi-threaded application to handle the different tasks. Or , suppose we make the any type of connections per user when the user enters the application. Now, there might be two issues:
1) There might be the limitation to JVM as how number of threads can be created in the application.
2) There might occur OutOfMemory error as there might be limit of memory to hold the Threads and Execute them.
Hence, we need to limit the number of users can enter in the application at a time but it is a big bottleneck for the application to limit the number of users. Hence , we are in trouble . What to do. Thanks to java for ThreadPool implementation. Let's see how ThreadPool handles this:
ThreadPool creates a number of worker Threads at any point of time. You will decide at least how many threads you will need to handle the requests of your application. We have separate methods of the utility class "Executors" for that purpose. Executors is a class and Executor is an interface. For Ex:
ExecutorService service = Executors.newFixedThreadPool(10); //ExecutorService is another interface which extends
Executor interface
Below three can be used to create a ThreadPool:
ExecutorService executorService1 = Executors.newSingleThreadExecutor(); //To create single thread
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
ExecutorService executorService3 = Executors.newScheduledThreadPool(10); // To create a number of threads with time scheduling
Now, the story come here like :- if you have passed 10 in the constructor then 10 worker threads would be created and the game starts.
We just need to submit our tasks to the Executor and it will pick any task from them and will execute it. Very clearly it means that these 10 threads would be called as worker threads. Suppose we have 100 tasks to be done and there are only 10 Threads created using ThreadPool. Now, all of these tasks would be busy executing those tasks one by one. Suppose one Thread has finished it's first task, then it will come in the idle state and will pick the another task from the Blocking Queue. What I mean by Blocking Queue is that to put the tasks in a collection , Blocking Queue is used. So the idle Thread would first Deblock the task from Blocking Queue and then would execute this. That's the magic of ThreadPool,using which we don't need to create hundred Thread at run time for processing hundred tasks. Rather 10 fix wroker threads done the work of hundred requests.
Hence, the work of Creation of Threads and work of Submission is separated by using ThreadPool.
Let's understand all this by the following Example:
public class ThreadPoolExample {
public static void main(String args[]) {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i =0; i<100; i++){
service.submit(new TaskToPerform(i));
}
}
}
In the above class, we have used the fixedThreadPool to create a ThreadPool of fixed size of 10. newFixedThreadPool is a static method of Executors class and the service reference is put in the ExecutorService interface. This class calls the submit method to submit 100 tasks (tasks here are the instances of TaskToPerform class). Hence, 10 threads are responsible to do 100 tasks. No new thread is getting created at run time and not .start() method is getting called from anywhere. All that is abstract to us. All that happends internally. Now, create the TaskToPerform thread.
final class TaskToPerform implements Runnable{
private int taskId;
public TaskToPerform(int id){
this.taskId = id;
}
@Override
public void run() {
System.out.println("Task ID : " + this.taskId +" performed by "
+ Thread.currentThread().getName());
}
}
Here, the Thread is created implementing Runnable interface. When we run this program produces the following output, Hence, it is proved that pool-1 is having multiple threads created and are being called one by one to perform the tasks.
Output:
Task ID : 0 performed by pool-1-thread-1
Task ID : 3 performed by pool-1-thread-4
Task ID : 2 performed by pool-1-thread-3
Task ID : 1 performed by pool-1-thread-2
Task ID : 5 performed by pool-1-thread-6
Task ID : 4 performed by pool-1-thread-5