Thursday, June 16, 2016

Executor Framework in Java with Examples

Being a java developer, I strongly recommend to have a thorough knowledge of this topic. Let's start the journey.

Let's go through some basic terms before jumping into the pool so that the things will be clear when we will use them.

* Process -> A program in execution which takes its separate memory space.
* Task -> A task is a logical unit of work.
* Thread -> The mechanism by which the Tasks can be executed asynchroneously. Asynchroneously means on the independent paths by not interfering into other tasks.

We have two policies for executing tasks using threads as follows:-

1) Execution of tasks sequentially using single thread.
2) Execution of each task in it's own thread.

In the first approach, one thread is created at all and the tasks are assigned to it in the sequential manner so that one task can be picked up after completion of another.

In the second approach, a separate thread is created for each task so that each task can be performed independently without waiting.

Executor framework takes birth here because both of the upper approaches have limitations as follows:

Problem with First Approach : It suffers from poor responsiveness and poor throughput.
Problem with Second Approach : It suffers with poor resource management as thread needs to be created for each task (for example,one million threads needed if there are million tasks).

Now the curiosity is how Executor Framework helps out in these scenarios.....

Executor Framework says that:
(i) Create a fixed number of threads in the application, suppose 10. These will be called as Worker Threads.
(ii) Create the tasks and submit it to executor to execute.

Suppose, we have created 10 fixed threads and we have 100 tasks to execute. Then, these ten threads will serve ten tasks at a time and when any task will be completed, the thread which executed this task is free for another task to be assigned to it. Hence the basic idea is , these 10 worker threads will execute these 100 tasks non stop. As and when any thread will be free, it will pick up the next task from the queue.
 
Now, let's discuss about tasks, where are these stored and how they are passed to executor. BlockingQueue is used for the purpose to hold the tasks. When any thread is free, it needs to dequeue the task from the BlockingQueue and execute it.

Executor : Executor is an interface having "void execute(Runnable command)" method.
Executors : Executors is a class having multiple methods to create the pool of worker threads.
ExecutorService : ExecutorService is an interface which extends Executor interface with some extra methods of submitting the tasks,
ScheduledExecutorService : ScheduledExecutorService is another interface with extends ExecutorService interface and provides some extra methods of scheduling the tasks.

Now, let's have an example to explain this all.

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

public class ExecutorDemo {

    private static ExecutorService executor = null;
    private static volatile Future firstThread = null;
    private static volatile Future secondThread = null;

    public static void main(String[] args) {
        executor = Executors.newFixedThreadPool(2);
        while (true)
        {
            try
            {
                checkTasks();
                Thread.sleep(1000);
            } catch (Exception e) {
                System.err.println("Caught exception: " + e.getMessage());
            }
        }
    }

    private static void checkTasks() throws Exception {
        if (firstThread == null
                || firstThread.isDone()
                || firstThread.isCancelled())
        {
            firstThread = executor.submit(new ThreadOne());
        }

        if (secondThread == null
                || secondThread.isDone()
                || secondThread.isCancelled())
        {
            secondThread = executor.submit(new ThreadTwo());
        }
    }
}

class ThreadOne implements Runnable {
    public void run() {
        while (true)
        {
            System.out.println("Executing thread one");
            try
            {
                Thread.sleep(1000);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

    }
}

class ThreadTwo implements Runnable {
    public void run() {
        while (true)
        {
            System.out.println("Executing thread two");
            try
            {
                Thread.sleep(1000);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }
}

First of all we have created two tasks here as ThreadOne and ThreadTwo. We created the threadpool of worker threads of size 2 by the method newFixedThreadPool(2). then the checkTasks method get called to check if any thread has been done or cancelled. If any task has been done or cancelled then it is getting submitted again to the executor by executor.submit(new ThreadTwo());.

Hence, we have created two threads but are not starting them explicitly, we are just submitting them to executor and these will be executed as per the execution logic.

No comments:

Post a Comment