Hello guys, in this blog we are going to talk about a very important topic, that is threads. We will first start with what is thread and how it is different from a process, how we can create a thread in different ways, and later on, we will deep dive into Executor Framework. So, are you guys excited?
Let us begin…
A process is a program that is in execution. For example, a java program is executed inside a JVM process. Each process is allocated a part of a RAM which is not shared with any other process. Now, let us talk about threads.
A thread is a segment of a process, that means a process can have multiple threads, all of them sharing the memory allocated to that process. Since a thread is a part of a process, it is considered as a lightweight component.
A thread can be in one of the following states
- New — Whenever a new thread is created, it is in this state. The execution of this thread is not yet started.
- Runnable — In this state, a thread has either started its execution or it is ready to be executed. The execution of a thread is handled by the thread scheduler
- Blocked or Waiting — A thread moves into this state when it is waiting for some operation to complete. For example — I/O Operation or trying to access a section which is currently locked by some other thread
- Terminated — A thread moves into this state when execution gets over normally or abnormally.
In Java, there are 2 ways of creating a thread.
- Extending Thread class
- Implementing Runnable interface
The most preferred way of creating a thread is by implementing the Runnable interface as it gives you the option of extending some other class. But, if you see the implementation of Thread class, it implements Runnable interface by overriding run method, which does nothing. The Thread class has a start method whose job is to create a new thread and then pass the run method to that thread for execution.
Here is an example of extending the Thread class.
And now let us see an example of implementing the Runnable interface.
There are various methods that you can call on a thread. Explore by yourself and then try it out by writing a program. Once you are confident, try implementing the producer-consumer problem.
Moving ahead, our next topic in this blog is about the Executor Framework.
The Executor Framework provides a thread pool facility to java applications. The main advantage of creating a thread pool in advance is that you don’t have to create a thread when a request comes in. Creating a thread for each request is an expensive operation.
The Executor Framework consists of 3 parts
- Executor — Unlike thread class which combines both task (run method) and its execution (start method), the Executor is simply a core interface that is used to specify the task.
- ExecutorService — It is an extension of the Executor interface and provides a facility for returning a Future object and terminate or shut down the thread pool. Once the shut down is called on the thread pool, it will not accept any new task but it will complete any pending task.
- Executors — It is a utility class that provides static methods to create different types of thread pools. The object returned by any of the static methods is the implementation of the ExecutorService interface.
Since ExecutorService being an extension of Executor, it provides two types of methods to execute a task. The first one is the execute method which takes in a Runnable object and returns nothing and the second method is the submit method which can either take Runnable object or Callable object and return a Future object holding the return value.
Let us see an example of how we can execute a runnable task by using the execute method.
It is very important to shut down the ExecutorService so as to terminate the thread pool.
Now, let us look into the example of submitting the runnable task to the Executor Framework.
Now if you see the above example, I have submitted the same Runnable task to two different overloaded submit methods. Before looking into each one of them, let us first talk about the Future.
The Future object is the way of getting the result from the thread once it completes its execution. You can check the status of the execution by simply calling the isDone method on the future object. To retrieve the result, we use the get method. There are chances that you might end up calling get method before thread completes its execution. In that case, the JVM doesn’t go to the next line of code until the execution of that thread completes. So, the best practice is to call get method only when you are interested in the result.
Now going back to the above example, the first submit method accepts a Runnable object. But, as you know the run method of the Runnable interface returns void. So, when you try to get on the future method, it will return null. But, that marks the execution of the thread as complete. The second overridden submit method accepts 2 arguments. The first one being the Runnable object and the second one is the value that you want to return once the execution of the thread is complete. So, when you call get method, you will get the value of the second argument as the result.
Below is the output of the above program
Now, let us look into the third overridden submit method which accepts the Callable object.
In this case, it can return any type of object. It totally depends on the implementation of the Callable interface.
The Executor Framework provides another type of interface named ScheduledExecutorService which allows you to schedule the task. This interface is an extension of the ExecutorService interface. It provides various methods, one of them is the schedule method which runs the task after a given delay of time. Here is an example of the same.
The Executor class has many methods to create a thread pool.
- newFixedThreadPool — It creates a fixed thread pool. The number of threads will depend on the integer value you provide to this method.
- newCachedThreadPool — It creates a new thread if none available otherwise it reuses the available ones from the thread pool.
- newScheduledThreadPool — Like newFixedThreadPool, it takes an integer value to create that many numbers of threads in a thread pool. Additional to that it allows you to execute a scheduled task.
- newSingleThreadExecutor — It consists of a single thread to execute a task.
In Java 8, a new type of thread pool was introduced as a newWorkStealingPool. It is based on a work-stealing algorithm. In this algorithm, if one thread finishes its work and has no more pending task, then it can steal the work from other thread’s queue. Internally, it makes use of ForkJoinPool which we will discuss in the next blog.
That’s it from this blog. I hope you guys learned something new. Do send me your feedback.