Thread Pools in Java

To perform new requests, a Java thread pool will be used that has already been set up. The overheads of the thread cycle and resource exhaustion are also addressed. Since threads are existing, this means that there is no more delay in the creation of a new thread as it waits for another request to arrive which will make your application much more responsive.

In addition, controlling the number of threads that are active simultaneously can be easier when you use Java thread pools.

What is a thread pool in Java?

In computing, a thread pool contains a set of pre-allocated threads that are adept at running tasks on demand. Using thread pools can drastically minimize resource consumption because the application does not create a new thread every time a thread is required.

Diagram:

thread pool diagram

How to create a thread pool in Java?

The java.util.concurrent package provides several classes that can be used for this purpose, including the Executors class and the ThreadPoolExecutor class.

Using the Executor class is the easiest way to create a thread pool, but the ThreadPoolExecutor class provides more flexibility and control.

Take a look at the code listing below that shows how you can work with the

ExecutorService class to create thread pools in Java:

import java.util.concurrent.*;
public class Techvidvan {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedTechvidvan(3);
executorService.submit(new Runnable() {
public void run() {
// Write your own collaboration
System.out.println("Inner run method...");
}
});

Use cases for thread pools:

Thread pools are often used in server applications to improve performance by creating a thread pool (with a maximum threshold) that can be used to process requests on a per-request basis instead of creating a new thread for each request.

For example, a web server uses a thread pool to process requests. When a new request comes in, the web server can create a new thread to handle that request. By using a thread pool, a web server can ensure that there are always enough threads available to handle incoming requests.

When Not to Use a Java Thread Pool:

If your application does not handle many threads, you can avoid using a thread pool. Threads take time to create and destroy, so using a thread pool in this situation would just add overhead with no appreciable benefit. In addition, the thread pool itself consumes resources.

Another situation where a developer would not want to use a thread pool is when your application requires threads to perform unrelated actions. For example, while one thread handles user events, another executes business logic, and another thread prints data.

Programmers should not use the thread pool if their application will be blocked for a long time. In this case, you should not use thread pools, because if there are too many blocked threads, jobs will not run at all.

Java Thread Pool Methods:

newFixedThreadPool(int s): The method creates a thread pool of fixed size s.

newCachedThreadPool(): The method creates a new thread pool that will spawn new threads when needed but will still use the previously created thread whenever they are available for use.

newSingleThreadExecutor(): Method creates a new thread.

Risks involved in Thread Pools:

Below are the risks associated with fiber pools.

Deadlocks:

It is a known fact that deadlocks can occur in any program that involves multi-threaded processing, and the thread pool is another deadlock scenario. Consider a scenario where all running threads are waiting for results from threads that are blocked and queued due to the unavailability of threads to run.

Thread leak:

A thread leak occurs when a thread is taken from the pool to perform a task but is not returned to the pool after the task completes. For example, when a thread throws an exception, and the pool class is not able to catch that exception, the thread is terminated, and the size of the thread pool is reduced by 1.

If the same thing is repeated multiple times, then there is a good chance that the pool is empty, and therefore, there are no threads available in the pool for carrying out other requests.

Resource Switching:

A lot of time is wasted in context switching between threads when the thread pool size is very large. Anytime there are more threads than the optimal number, this can cause a starvation problem and lead to a waste of resources.

Points to Remember:

  • Don’t wait for tasks that are waiting at the same time to benefit from other tasks. As we mentioned above, this can lead to a fatal situation. Caution should be exercised when using threads for long-term operations. It can cause the job to sit indefinitely and eventually cause the product to leak.
  • In the end, the issue can only come to a clear end. If this does not happen, the program will continue to run and will not finish. Call the pool’s close() method to determine the worker. Note that the Decision will be cancelled if someone tries to submit another job to the admin after it has been closed.
  • Understanding the activities required to get the most out of the pool. If tasks are assigned differently, then we need to find pools that do different types of tasks so we can adjust accordingly.
  • To reduce the possibility of the JVM running out of memory, you can control the maximum number of threads that can run on the JVM. Once the pool reaches its maximum size, it cannot create new threads.
  • If threading is done, the thread pool uses the same thread. Therefore, time and resources used to create new threads are saved.
  • Don’t wait for tasks that are waiting at the same time to benefit from other tasks. As we mentioned above, this can lead to a fatal situation. Caution should be exercised when using threads for long-term operations. It can cause the job to sit indefinitely and eventually cause the product to leak.
  • In the end, the issue can only come to a clear end. If this does not happen, the program will continue to run and will not finish. Call the pool’s close() method to determine the worker.
  • Note that the Decision will be cancelled if someone tries to submit another job to the admin after it has been closed. Understanding the activities required to get the most out of the pool.
  • If tasks are assigned differently, then we need to find pools that do different types of tasks so we can adjust accordingly. To reduce the possibility of the JVM running out of memory, you can control the maximum number of threads that can run on the JVM.
  • Once the pool reaches its maximum size, it cannot create new threads. If threading is done, the thread pool uses the same thread. Therefore, time and resources used to create new threads are saved.

Thread pool executor:

The ThreadPoolexecutorr is an extensible thread pool implementation with lots of parameters and hooks for fine-tuning. The main configuration parameters that we’ll discuss here are corePoolSize, maximumPoolSize and keepAliveTime.The pool consists of a fixed number of core threads that are kept inside all the time. It also consists of some excessive threads that may be spawned and then terminated when they are no longer needed.

newFixedThreadPool:

Let’s look at an example. The newFixedThreadPool method creates a ThreadPoolExecutor with the same corePoolSize and maximumPoolSize parameter values ​​and a zero keepAliveTime.

This means that the number of threads in this thread pool is always the same:

ThreadPoolExecutor executor =
  (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
executor.submit(() -> {
    Thread.sleep(1000);
    return null;
});
executor.submit(() -> {
    Thread.sleep(1000);
    return null;
});
executor.submit(() -> {
    Thread.sleep(1000);
    return null;
});

asEquals(2, executor.getPoolSize());
asseEquals(1, executor.getQueue().size());
}
Executors.newCachedThreadPool()
We can create another preconfigured ThreadPoolExecutor using the Executors.newCachedThreadPool() method. This method does not accept a thread count at all. We set CorePoolSize to 0 and set maximumPoolSize to Integer.MAX_VALUE. Finally, the keepAliveTime is 60 seconds:
ThreadPoolExecutor executor =
  (ThreadPoolExecutor) Executors.newCachedThreadPool();
executor.submit(() -> {
    Thread.sleep(1000);
    return null;
});
executor.submit(() -> {
    Thread.sleep(1000);
    return null;
});
executor.submit(() -> {
    Thread.sleep(1000);
    return null;
});

assertEquals(3, executor.getPoolSize());
ssesEquals(0, executor.getQueue().size());
}

Thread pool program:
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.text.SimpleDateFormat;
  
  
class Tasks implements Runnable
{
private String taskName;
public tasks (string str)
{
taskName = str;
}
public void run()
{
Try
{
for (int j = 0; j <= 5; j++)
{
if (j == 0)
{
Date dt = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("hh : mm : ss");
System.out.println("Initialization time for job name: "+ job name + " = " + sdf.format(dt));
  
}
other
{
Date dt = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("hh : mm : ss");
System.out.println("Execution time for jobname: " + jobname + " = " +sdf.format(dt));
  
}
Thread.sleep(1000);
}
  
System.out.println(taskname + "is complete.");
}
  
catch (InterruptedException ie)
{
i.e. printStackTrace();
}
}
}
  
public class ThreadPoolExample
{
static final int MAX_TH = 3;
public static void main(String argvs[])
{
Runnable rb1 = new Tasks("task 1");
Runnable rb2 = new Tasks("task 2");
Runnable rb3 = new Tasks("task 3");
Runnable rb4 = new Tasks("task 4");
Runnable rb5 = new Tasks("task 5");
ExecutorService pl = Executors.newFixedThreadPool(MAX_TH);
pl.execute(rb1);
pl.execute(rb2);
pl.execute(rb3);
pl.execute(rb4);
pl.execute(rb5);
pl.shutdown();
}
}

Advantages of Java Thread Pool

The advantages of the thread pool are as follows:

Increased performance:

for every task, thread pools can reuse threads rather than create and destroy them, which cuts costs and enables more effective use of resources. Increased resource management control: you can manage the resources that are available to your application and prevent it from being overrun with a high amount of threads if you limit the number of threads in this pool.

Greater control over resource management:

by limiting the number of threads in the pool, you can manage the resources that are available to your application and prevent it from being overrun with too many threads.

Better control over job execution:

thread pools provide you with better control of the number of jobs that are being run concurrently, as well as how much queueing is happening. It may help you prevent your application from being overwhelmed by the amount of resources or out of memory.

Disadvantages:

The priority and state of the thread you are using is not possible to check. Do not give the thread a fixed identifier, and you will not be able to follow it. If there is a large demand in the thread supply, this process will be discontinued.

Conclusion

A thread pool is a great way of improving the responsiveness of your Java applications by reusening threads and avoiding unnecessary creation of new threads for each task.

The number of threads used at any one time is restricted by a thread pool not only as regards the allocation of resources between individual threads, but also with regard to their use over an extended period.

When running an application, thread pooling is a way of creating several threads at once. The pool will handle the creation, allocation and termination of threads.