Recycle Threads and Save Resources
Before we begin to talk about thread pool, let revisit threads. Threads are really powerful things. By creating multiple threads, multithreading, you can do ...
Join the DZone community and get the full member experience.Join For Free
Hey, Tea Lovers! Before we begin to talk about thread pool, lets revisit threads. Threads are really powerful things. By creating multiple threads, multithreading, you can do tasks simultaneously. Wow, how cool and how efficient, right? But wait, if there is a heaven, there is a hell. Multithreading does come with thread management overhead for the developer, such as creating multiple threads, the synchronicity between them, deadlock, and most importantly, understanding how efficient it really is.
In this post, we will talk about how multithreading affects your system without you knowing it, how to overcome certain parts of it, and how to make it more efficient, including thread reuse or recycling. If you want to learn about multithreading in Java or want to refresh the basics you can see my previous post "How MultiThreading Lets You Drive A Car, Ride The Bikes, And Sail A Boat Simultaneously" [here].
An efficient and super fast program is the dream of every programmer. To make this happen, we usually end up using threads, multithreading precisely. Most of the time we create so many threads that we are actually decreasing the speed of our program instead of making it faster. How? Creating so many threads is a very expensive process, and it can delay the actual process which you wanted to avoid in the first place.
In Java, threads are mapped to system-level threads, so over numbering them can empty your resources. Once the
run() method finishes, we never use that thread again and end up creating new threads again and again. Resulting in thread creation overhead. And there are other factors as well, such as your CPU. If we end up with too many threads that our COU, most of the time gets spent in context switching. So, how will you avoid these multiple new thread creation? Just sip your tea and read. For the code, you can find it on GitHub or the full project here.
Recycling Is Good for the Environment: Thread Pool
Recycling is good for our environment since the resources are limited on our planet.
Similarly, our computer resources are also limited. So, why not reuse them and save efficiency? Reuse the thread we used already instead of creating a new one?
How Does Thread Pooling or Any Pooling Works
What is pooling by the way? In a simpler word, combining resources for sharing. Examples can be your Uber pool, printer sharing in your office, Dynamic Host Configuration Protocol (DHCP), JDBC connection pooling, and many more. I have explained pooling in more detailed in How to Achieve Greatness in JDBC Performance [here] and JDBC Connection Pooling Explained with HikariCP [here].
In thread pooling or any pooling, you collect N number of threads or resource in one place. You pick one thread from it, use it, and put it back into the pool after completing your work. Well, it's the simplest way of explaining it. There can be certain scenarios like all the resources are busy when you went to pick it up, you wait or add one more resource if possible. These are the basic conditions and scenarios of any pooling system.
Thread Pool Continued
Now that we know about the pooling, let us jump to the actual working of thread pooling in Java. To use the Threadpool in Java, Java provides a framework called Executor Framework, which is an interface called
Executor and its subinterface
ExecutorService and the classes implementing them. Don't worry, it's not another dependency. It is in the package
Executor contains only one abstract method called
execute(Runnable task). Through this, you can execute your tasks. Tasks are just
Thread Pool ExecutorService
ExecutorService is a subinterface of the
Executor interface. It is a pro version you can say. It has multiple methods that give us more control over tasks. To execute a task, you can use the
execute(Runnable task) method. There is a method
submit(..) that works similarly to
execute, but returns a
Future<T>, which, as the name suggests, gives you a future value.
Future basically says that "I may or may not have the value right now, but it could be available in the future if the task is completed and produces some result". It deserves its own post, and we will do that in future update here. But, how can you create the object or thread pool of
Thread Pool Executors
Executors is a kind of factory/utility class for the
ExecutorService. It creates the objects of
ExecutorService. There are various ways you can create different types of pools.
|newSingleThreadExecutor()||Creates a single thread.|
|newFixedThreadPool(int size)||Creates a fixed size thread pool.|
|newCachedThreadPool()||Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads if they are available|
In the case of
newFixedThreadPool(int size) and
newSingleThreadExecutor(), the threads need to wait if the size is full and no thread is available to use. But in the case of
newCachedThreadPool(), a new task doesn't need to wait if the thread is not available.
Make sure to use
shutdown()after all the task has been assigned or the program will not stop as the thread pool will still be active.
Understand Java ThreadPool With Examples
Let us rewrite the example from "How MultiThreading Lets You Drive A Car, Ride The Bikes, And Sail A Boat Simultaneously" using the thread pool. In the example, you have boats, cars, and bikes. In the previous example, we created a new thread for each vehicle. Now, we will utilize our new understanding of thread pools.
The Tasks: Car, Bike, and Boat
These are the
Runnable objects we will be using. And there are one utility function pring5Times(String statement) to print the given statement 5 times. Let us see each function one-by-one.
It creates only one thread in the pool, and each task needs to wait for the previous one to complete to use the thread. In this, sequential execution takes place.
As you can see, threads are running one-by-one, since there is only one thread to be shared.
This creates a poll with the given number of threads i.e
limit. The number of tasks that will run in parallel is the given limit. Other task needs to wait for a thread to complete. So, in our example, we have created a pool with size 2. So,
Bike will run in parallel or simultaneously, but the Boat needs to wait for either of them to complete.
Bike and Car ran simultaneously, but the boat needed to wait since only 2 threads were available.
This method creates a unique and dynamic pool. If the thread is available, it gets assigned to the task. If not, a thread is added in the pool. In this, a thread does not wait for other threads to complete. The best suitable scenario would be when you have small tasks that need to be run simultaneously.
The Tasks are the same as the above example but this time all three of the tasks,
Boat would run simultaneously.
As you can see, you were able to drive, ride, and sail simultaneously.
We have looked into the bad side of multithreading and its overhead on the system, what is thread pool and how to use them, type of thread pool. I hope you enjoyed the post. In the next post on concurrency, we will talk about
CompletableFuture. You can find the code on GitHub here or the full project here. You may also like, TV Series Every Programmer Should Watch, Reduce the null pointer with the help of Optional and say goodbye to boilerplate code with Lombok.
Published at DZone with permission of Imran Shaikh. See the original article here.
Opinions expressed by DZone contributors are their own.