Suppose in an application , large number of tasks needs to be completed. It is practically not possible to do all these tasks with separate threads for each task.Creating large number of threads in a Java process is very difficult for the JVM to manage.The concept ThreadPool in Java is useful in situations involving so many tasks . A pool of threads can be used for completing those tasks.The number of threads will be limited in this case.
ThreadPool in Java
Here there are a number of threads in the pool . Once a new task comes , the task is adding to a queue. From the queue that task is taking by any of the threads in the pool. When all threads are busy with tasks , then the new tasks coming is keeping in the queue. Those tasks will be taken by any of the threads in the pool soon.
ThreadPool in Java example
Here , we are discussing the concept of thread pool with an example.(Java is providing a ThreadPoolExecutor in concurrent package to manage pool of threads.If we are using that directly , the overburden of creating and managing threads will be gone).
The first class given here is the ThreadPool.java . It is having a LinkedBlockingQueue to keep the incoming tasks. It is having an array of Service objects. The Service object is nothing but a thread object . That also given below as the second code snippet.ThreadPool is starting the threads . Once a new task comes the task is passing to the execute method , where the task is adding to the queue of ThreadPool and notifying all the Service threads. If any of the Service thread is waiting to get a task , the new task is giving to that Service and it will be executed .
import java.util.concurrent.LinkedBlockingQueue;
public class ThreadPool {
private int poolSize = 0;
private LinkedBlockingQueue
private Service[] services = null;
public ThreadPool(int size) {
this.poolSize = size;
services = new Service[size];
this.queue = new LinkedBlockingQueue
startServices();
}
private void startServices() {
for (int i = 0; i < poolSize; i++) {
services[i] = new Service(queue);
services[i].start();
}
}
public void execute(Runnable task) {
synchronized (queue) {
queue.add(task);
queue.notify();
}
}
}
Now let us see the Service.java. It is nothing but a thread object. The ThreadPool.java class shown above has an array of Service objects. These are threads which can access the same LinkedBlockingQueue of ThreadPool.java.So the Service thread can take the task added in ThreadPool.
import java.util.concurrent.LinkedBlockingQueue;
public class Service extends Thread {
private LinkedBlockingQueue
private Runnable task = null;
public Service(LinkedBlockingQueue
super(name);
this.queue = queue;
}
public void run() {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
try {
task = (Runnable) queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
task.run();
}
}
}
If there is no task in the queue , then the Service thread is waiting on queue.Once a new task comes , Service thread moves out from the wait and takes that task. And then executes the task. Our task is Runnable but we are not executing the task with start() method.Because we are not going to start a new thread for each Runnable ,but executing the task in the current service thread.
Now let us see the main class.The main class is creating a ThreadPool instance with number of Services as 3 .And creating four Runnable tasks.
1)First task is printing multiples of 10 from 0 to 50
2)Second task is printing multiples of 10 from 50 to 100
3)Third task is printing multiples of 10 from 100 to 150
4)Fourth task is printing multiples of 10 from 150 to 200
Before printing the next number , a sleep time of 5 seconds is given .It is to see the working correctly.
public class ThreadPoolMain {
public ThreadPoolMain() {
}
public static void main(String[] args) {
ThreadPool pool = new ThreadPool(3);
Runnable firstTask = new Runnable() {
int number = 0;
public void run() {
while (number < 50) {
System.out.println("Thread :" + Thread.currentThread().getName() + ": Number = " + number);
number = number + 10;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable secondTask = new Runnable() {
int number = 50;
public void run() {
while (number < 100) {
System.out.println("Thread :" + Thread.currentThread().getName() + ": Number = " + number);
number = number + 10;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable thirdTask = new Runnable() {
int number = 100;
public void run() {
while (number < 150) {
System.out.println("Thread :" + Thread.currentThread().getName() + ": Number = " + number);
number = number + 10;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable fourthTask = new Runnable() {
int number = 150;
public void run() {
while (number < 200) {
System.out.println("Thread :" + Thread.currentThread().getName() + ": Number = " + number);
number = number + 10;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
pool.execute(firstTask);
pool.execute(secondTask);
pool.execute(thirdTask);
pool.execute(fourthTask);
}
}
Now let us see the output.Here three Service threads are starting.So 3 out of the four tasks will be taken.Once any of the service finishes its task , it takes the remaining task.When queue is empty the threads are waiting. The working can be well understood if we use any Java profiler.( If we are using newer versions of jdk then the bin directory of JDK contains a JVisualVM.exe. if we profile our application with VisualVM , we can see the status of each thread in the pool) .Remember , this application is not exiting itself , because the Service threads are executing for the whole time(because of the while(true) in run() method )
Output
Thread :Service2: Number = 0
Thread :Service1: Number = 100
Thread :Service0: Number = 50
Thread :Service1: Number = 110
Thread :Service0: Number = 60
Thread :Service2: Number = 10
Thread :Service1: Number = 120
Thread :Service0: Number = 70
Thread :Service2: Number = 20
Thread :Service1: Number = 130
Thread :Service0: Number = 80
Thread :Service2: Number = 30
Thread :Service1: Number = 140
Thread :Service0: Number = 90
Thread :Service2: Number = 40
Thread :Service1: Number = 150
Thread :Service1: Number = 160
Thread :Service1: Number = 170
Thread :Service1: Number = 180
Thread :Service1: Number = 190
You can change the number of threads in the main method and you can see the difference .
See Related Discussions
The join() method in Threading
The yield() method in Threading