Wednesday, November 28, 2018

Thread Pooling In Java Using Executors Class

Most of the Important and most of the developer found one topic in java they perform a task and then they get destroyed. Purpose of multithreading is to perform multiple tasks simultaneously for effective utilization of our resources (time and CPU).


Imagine that there are 10 tasks to perform, so using traditional multithreading mechanism we will need 10 threads. Do you see a drawback in this approach? I see it; creation of a new thread per task will cause resource overhead.

Let me try to connect it with a real-life analogy, imagine in an IT company there are 10 tasks to be done, assigning 1 person 1 task will speed up the process but at the same time it will require 10 person, so a huge resource overhead, imagine what will happen if there are 1000 tasks will the company hire 1000 people. What if 5 persons could have also done the 10 tasks in similar time ie once resource got free he could have picked the next task and coincidentally this is the approach which is used in organizations for managing work.

Same concept is used in thread pooling. Thread pool, means a group of thread ready to perform tasks as they come. The main USP of threads in threads pool are that threads are not destroyed but reused.
Let us assume 7 tasks come up.

In traditional thread model: 7 threads will be created for handling 7 tasks.

Thread Pooling: Let’s assume that we created a fixed thread pool of size 3, lets call these threads A, B, C. Now task 1 comes up and A picks it, now task2 comes up while A is handling it, so task2 is assigned to B, now task3 comes and both A and B are busy so task3 is assigned to C. Now if task4 comes up at this time and if any of A, B,C are free it will be auto assigned to any of free threads , only if all threads are busy then task4 will be queued and be assigned as soon as A,B,C . Do you see how effectively thread pooling handles resource overhead.




Lets deep dive into how Thread Pooling is implemented in Java:

To use thread pooling in java we have to use Executor or ThreadPoolExecutor class, which are present in java.util.concurrent package.

In this blog we will see Executors class

This class has 2 factory methods which are used to implement thread pooling along with some fixed configurations.

The Executors class supports both Runnable and Callable interfaces. In Callable interface we return a value instead of void.

The runnable tasks are submitted to the executor service using either execute() or submit() methods.
The callable tasks are always submitted to the executor service using submit() method.

public static ExecutorService newFixedThreadPool(int poolsize)

This method creates thread pool of fixed size as tasks are submitted, up to the poolSize, and then attempts to keep the pool size constant (adding new threads if a thread dies due to an unexpected Exception). If additional tasks are submitted when all threads are busy, they will wait in the queue until a thread is available.

Also Read: Java thread wait, notify and notifyAll Methods Examples

Let’s see a code example:


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

class Task implements Runnable{

       String name;

       public Task(String name)
       {
              this.name=name;
       }

       @Override
       public void run() {
          
              System.out.println("Stubbed business logic of "+this.name+" done by:"+Thread.currentThread().getName());

              try {
                     Thread.sleep(5*1000);
              }
              catch(InterruptedException e)
              {
                     System.out.println(e);
              }

       }

}

public class ThreadPooling {
       public static void main(String[] args) {
              ExecutorService threadPool=Executors.newFixedThreadPool(3);
              threadPool.execute(new Task("Printing"));
              threadPool.execute(new Task("Monitoring"));
              threadPool.execute(new Task("Tracking"));
              threadPool.execute(new Task("Billing"));
              threadPool.execute(new Task("Managing"));
              threadPool.execute(new Task("Transactioning"));
              threadPool.shutdown();

       }
}

Output:

Stubbed business logic of Tracking done by:pool-1-thread-3
Stubbed business logic of Printing done by:pool-1-thread-1
Stubbed business logic of Monitoring done by:pool-1-thread-2
Stubbed business logic of Billing done by:pool-1-thread-3
Stubbed business logic of Managing done by:pool-1-thread-1
Stubbed business logic of Transactioning done by:pool-1-thread-2

Code Explanation:



ExecutorService threadPool=Executors.newFixedThreadPool(3);

Here a fixed thread pool of max size 3 is created. It returns an object of ExecutorService. This object can then be used to implement thread pooling.
ExecutorService interface methods are: execute, submit, shutdown and shutDownNow. We will see there use below


threadPool.execute(new Task("Printing"));

Here execute method is used to run a task, by thread.

Now assume
  1.  3 tasks are submitted
  2.  Assume all 3 threads are created and being used
  3.  Now the 4th task is submitted
  4.  The 4th task will wait in queue
  5.  Assume thread 2 gets free then it will take up 4th task.

So thread in thread pool will never die it will be reused.



threadPool.shutdown();

Threads in thread pool will not be destroyed explicitly, if we want to destroy threads in thread pool explicitly then we need to use thread pool.shutdown method.It will destroy threads gracefully once all threads are idle and no task in the queue.
threadPool.shutdownNow();
It will terminate the threads immediately. It does not see if tasks are in queue or thread is processing a task so it does not do graceful termination.


Note:



ExecutorService threadPool=Executors.newFixedThreadPool(3);

threadPool.execute(new Task("Printing"));

In case only 1 task is submitted then at that time only 1 thread will be created. (Don’t be confused that at first time only 3 threads are created, specifying 3 means max 3 threads will be created, irrespective of a number of tasks being submitted.)
2nd thread will be created only when 1st thread is busy in doing the task.
public static ExecutorService newCachedThreadPool()

A cached thread pool differs from fixed thread pool in 2 ways.
1)      In fixed thread pool we had to specify no of threads eg in last case we said 3 and now if 6 tasks came 3 threads could effectively manage it, what if 60 tasks came then 3 would not have been idle, so in fixed thread pool we need to know number of tasks and then define the number of threads to manage thread pooling. What if I want to be free from this responsibility, cached thread pool offers us this flexibility.No need to specify the number of threads, it will automatically create threads. ( Analogous to Cloud, where resources grow and shrink based on demand, fixed thread pool is more like on premise systems)
2)      Secondly it will automatically remove idle threads (by default 60s), when there is no pending tasks in queue and thread has not been doing any task, no need to use shutdown  method

Thus it is more flexible. Let’s see a code example


class Task implements Runnable{
       String name;
       public Task(String name)
       {
              this.name=name;
       }

       @Override
       public void run() {

              System.out.println("Stubbed business logic of "+this.name+" done by:"+Thread.currentThread().getName());
       }
     

}

public class ThreadPooling {

       public static void main(String[] args) throws InterruptedException {

              ExecutorService threadPool=Executors.newCachedThreadPool();

              for(int i=1;i<=20;i++)

              {

              threadPool.execute(new Task("Task"+i));

              }
       }

}
Output
Stubbed business logic of Task2 done by:pool-1-thread-2
Stubbed business logic of Task1 done by:pool-1-thread-1
Stubbed business logic of Task4 done by:pool-1-thread-4
Stubbed business logic of Task8 done by:pool-1-thread-1
Stubbed business logic of Task7 done by:pool-1-thread-4
Stubbed business logic of Task5 done by:pool-1-thread-5
Stubbed business logic of Task6 done by:pool-1-thread-2
Stubbed business logic of Task13 done by:pool-1-thread-1
Stubbed business logic of Task12 done by:pool-1-thread-4
Stubbed business logic of Task10 done by:pool-1-thread-2
Stubbed business logic of Task16 done by:pool-1-thread-4
Stubbed business logic of Task15 done by:pool-1-thread-2
Stubbed business logic of Task18 done by:pool-1-thread-8
Stubbed business logic of Task19 done by:pool-1-thread-2
Stubbed business logic of Task11 done by:pool-1-thread-5
Stubbed business logic of Task20 done by:pool-1-thread-4
Stubbed business logic of Task17 done by:pool-1-thread-1
Stubbed business logic of Task9 done by:pool-1-thread-6
Stubbed business logic of Task3 done by:pool-1-thread-3
Stubbed business logic of Task14 done by:pool-1-thread-7

Here if we analyse automatically 20 tasks are handled by 8 threads in pool.

Comparing it will fixed pool, if we used the code
ExecutorService threadPool=Executors.newFixedThreadPool(5);

              for(int i=1;i<=20;i++)

              {

              threadPool.execute(new Task("Task"+i));

              }

              threadPool.shutdown();
Then 5 threads would have taken care of handling these 20 tasks

Sample Output

Stubbed business logic of Task2 done by:pool-1-thread-2
Stubbed business logic of Task5 done by:pool-1-thread-5
Stubbed business logic of Task7 done by:pool-1-thread-5
Stubbed business logic of Task8 done by:pool-1-thread-5
Stubbed business logic of Task9 done by:pool-1-thread-5
Stubbed business logic of Task10 done by:pool-1-thread-5
Stubbed business logic of Task11 done by:pool-1-thread-5
Stubbed business logic of Task12 done by:pool-1-thread-5
Stubbed business logic of Task13 done by:pool-1-thread-5
Stubbed business logic of Task14 done by:pool-1-thread-5
Stubbed business logic of Task15 done by:pool-1-thread-5
Stubbed business logic of Task16 done by:pool-1-thread-5
Stubbed business logic of Task17 done by:pool-1-thread-5
Stubbed business logic of Task18 done by:pool-1-thread-5
Stubbed business logic of Task19 done by:pool-1-thread-5
Stubbed business logic of Task20 done by:pool-1-thread-5
Stubbed business logic of Task4 done by:pool-1-thread-4
Stubbed business logic of Task1 done by:pool-1-thread-1
Stubbed business logic of Task3 done by:pool-1-thread-3
Stubbed business logic of Task6 done by:pool-1-thread-2




If no thread pooling had been used ( Traditional multithreading )


public static void main(String[] args) {

              for(int i=1;i<=20;i++) {

              Thread t=new Thread(new Task("task"+i));

              t.start();

              }

       }
Then automatically 20 threads would have been created:
Stubbed business logic of task1 done by:Thread-0
Stubbed business logic of task2 done by:Thread-1
Stubbed business logic of task5 done by:Thread-4
Stubbed business logic of task3 done by:Thread-2
Stubbed business logic of task4 done by:Thread-3
Stubbed business logic of task9 done by:Thread-8
Stubbed business logic of task7 done by:Thread-6
Stubbed business logic of task8 done by:Thread-7
Stubbed business logic of task6 done by:Thread-5
Stubbed business logic of task12 done by:Thread-11
Stubbed business logic of task11 done by:Thread-10
Stubbed business logic of task10 done by:Thread-9
Stubbed business logic of task14 done by:Thread-13
Stubbed business logic of task13 done by:Thread-12
Stubbed business logic of task18 done by:Thread-17
Stubbed business logic of task15 done by:Thread-14
Stubbed business logic of task20 done by:Thread-19
Stubbed business logic of task17 done by:Thread-16
Stubbed business logic of task16 done by:Thread-15
Stubbed business logic of task19 done by:Thread-18





So lets tabulate the result
No thread pool
20 threads created
With cached pool
8 threads JVM created
With fixed pool
We need to give no of threads eg we gave 5 for 20 and it effectively managed, so we need to use our experience to determine no of threads or use cached pool

Number of threads:

So 20 (traditional) vs 8 (thread pool), seems a great performance improvement here.
I believe you are now able to understand the power of thread pooling.
We will see more detail about ThreadPoolExecutor class and Callable interface in later posts.

If you have any issue please leave us a comment.




0 comments:

Post a Comment