Tutorial

ThreadPoolExecutor - Java Thread Pool Example

Published on August 3, 2022
author

Pankaj

ThreadPoolExecutor - Java Thread Pool Example

Java thread pool manages the pool of worker threads. It contains a queue that keeps tasks waiting to get executed. We can use ThreadPoolExecutor to create thread pool in Java. Java thread pool manages the collection of Runnable threads. The worker threads execute Runnable threads from the queue. java.util.concurrent.Executors provide factory and support methods for java.util.concurrent.Executor interface to create the thread pool in java. Executors is a utility class that also provides useful methods to work with ExecutorService, ScheduledExecutorService, ThreadFactory, and Callable classes through various factory methods. Let’s write a simple program to explain it’s working. First, we need to have a Runnable class, named WorkerThread.java

package com.journaldev.threadpool;

public class WorkerThread implements Runnable {
  
    private String command;
    
    public WorkerThread(String s){
        this.command=s;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+" Start. Command = "+command);
        processCommand();
        System.out.println(Thread.currentThread().getName()+" End.");
    }

    private void processCommand() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString(){
        return this.command;
    }
}

ExecutorService Example

Here is the test program class SimpleThreadPool.java, where we are creating fixed thread pool from Executors framework.

package com.journaldev.threadpool;

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

public class SimpleThreadPool {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread("" + i);
            executor.execute(worker);
          }
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
        System.out.println("Finished all threads");
    }
}

In the above program, we are creating a fixed-size thread pool of 5 worker threads. Then we are submitting 10 jobs to this pool, since the pool size is 5, it will start working on 5 jobs and other jobs will be in wait state, as soon as one of the job is finished, another job from the wait queue will be picked up by worker thread and get’s executed. Here is the output of the above program.

pool-1-thread-2 Start. Command = 1
pool-1-thread-4 Start. Command = 3
pool-1-thread-1 Start. Command = 0
pool-1-thread-3 Start. Command = 2
pool-1-thread-5 Start. Command = 4
pool-1-thread-4 End.
pool-1-thread-5 End.
pool-1-thread-1 End.
pool-1-thread-3 End.
pool-1-thread-3 Start. Command = 8
pool-1-thread-2 End.
pool-1-thread-2 Start. Command = 9
pool-1-thread-1 Start. Command = 7
pool-1-thread-5 Start. Command = 6
pool-1-thread-4 Start. Command = 5
pool-1-thread-2 End.
pool-1-thread-4 End.
pool-1-thread-3 End.
pool-1-thread-5 End.
pool-1-thread-1 End.
Finished all threads

The output confirms that there are five threads in the pool named from “pool-1-thread-1” to “pool-1-thread-5” and they are responsible to execute the submitted tasks to the pool.

ThreadPoolExecutor Example

Executors class provide simple implementation of ExecutorService using ThreadPoolExecutor but ThreadPoolExecutor provides much more feature than that. We can specify the number of threads that will be alive when we create ThreadPoolExecutor instance and we can limit the size of thread pool and create our own RejectedExecutionHandler implementation to handle the jobs that can’t fit in the worker queue. Here is our custom implementation of RejectedExecutionHandler interface.

package com.journaldev.threadpool;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

public class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println(r.toString() + " is rejected");
    }

}

ThreadPoolExecutor provides several methods using which we can find out the current state of the executor, pool size, active thread count and task count. So I have a monitor thread that will print the executor information at a certain time interval.

package com.journaldev.threadpool;

import java.util.concurrent.ThreadPoolExecutor;

public class MyMonitorThread implements Runnable
{
    private ThreadPoolExecutor executor;
    private int seconds;
    private boolean run=true;

    public MyMonitorThread(ThreadPoolExecutor executor, int delay)
    {
        this.executor = executor;
        this.seconds=delay;
    }
    public void shutdown(){
        this.run=false;
    }
    @Override
    public void run()
    {
        while(run){
                System.out.println(
                    String.format("[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, isShutdown: %s, isTerminated: %s",
                        this.executor.getPoolSize(),
                        this.executor.getCorePoolSize(),
                        this.executor.getActiveCount(),
                        this.executor.getCompletedTaskCount(),
                        this.executor.getTaskCount(),
                        this.executor.isShutdown(),
                        this.executor.isTerminated()));
                try {
                    Thread.sleep(seconds*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
            
    }
}

Here is the thread pool implementation example using ThreadPoolExecutor.

package com.journaldev.threadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class WorkerPool {

    public static void main(String args[]) throws InterruptedException{
        //RejectedExecutionHandler implementation
        RejectedExecutionHandlerImpl rejectionHandler = new RejectedExecutionHandlerImpl();
        //Get the ThreadFactory implementation to use
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        //creating the ThreadPoolExecutor
        ThreadPoolExecutor executorPool = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2), threadFactory, rejectionHandler);
        //start the monitoring thread
        MyMonitorThread monitor = new MyMonitorThread(executorPool, 3);
        Thread monitorThread = new Thread(monitor);
        monitorThread.start();
        //submit work to the thread pool
        for(int i=0; i<10; i++){
            executorPool.execute(new WorkerThread("cmd"+i));
        }
        
        Thread.sleep(30000);
        //shut down the pool
        executorPool.shutdown();
        //shut down the monitor thread
        Thread.sleep(5000);
        monitor.shutdown();
        
    }
}

Notice that while initializing the ThreadPoolExecutor, we are keeping initial pool size as 2, maximum pool size to 4 and work queue size as 2. So if there are 4 running tasks and more tasks are submitted, the work queue will hold only 2 of them and the rest of them will be handled by RejectedExecutionHandlerImpl. Here is the output of the above program that confirms the above statement.

pool-1-thread-1 Start. Command = cmd0
pool-1-thread-4 Start. Command = cmd5
cmd6 is rejected
pool-1-thread-3 Start. Command = cmd4
pool-1-thread-2 Start. Command = cmd1
cmd7 is rejected
cmd8 is rejected
cmd9 is rejected
[monitor] [0/2] Active: 4, Completed: 0, Task: 6, isShutdown: false, isTerminated: false
[monitor] [4/2] Active: 4, Completed: 0, Task: 6, isShutdown: false, isTerminated: false
pool-1-thread-4 End.
pool-1-thread-1 End.
pool-1-thread-2 End.
pool-1-thread-3 End.
pool-1-thread-1 Start. Command = cmd3
pool-1-thread-4 Start. Command = cmd2
[monitor] [4/2] Active: 2, Completed: 4, Task: 6, isShutdown: false, isTerminated: false
[monitor] [4/2] Active: 2, Completed: 4, Task: 6, isShutdown: false, isTerminated: false
pool-1-thread-1 End.
pool-1-thread-4 End.
[monitor] [4/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [0/2] Active: 0, Completed: 6, Task: 6, isShutdown: true, isTerminated: true
[monitor] [0/2] Active: 0, Completed: 6, Task: 6, isShutdown: true, isTerminated: true

Notice the change in active, completed and total completed task count of the executor. We can invoke shutdown() method to finish execution of all the submitted tasks and terminate the thread pool. If you want to schedule a task to run with delay or periodically then you can use ScheduledThreadPoolExecutor class. Read more about them at Java Schedule Thread Pool Executor.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Pankaj

author

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
April 27, 2013

Thank you for your posts. I have learned a lot from your tutorials.

- Lin

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    August 27, 2013

    Thanks for the Tutorial. Really Good.

    - Roshan

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      October 2, 2013

      Thanks for the nice example I have a question. What is the use of passing 3 as an argument while creating an object of MyMonitorThread ?

      - Rahul

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        October 22, 2013

        thanks a lot

        - thanksmahesh

          JournalDev
          DigitalOcean Employee
          DigitalOcean Employee badge
          November 1, 2013

          Thanks a lot for your great article.

          - Tumi Le

            JournalDev
            DigitalOcean Employee
            DigitalOcean Employee badge
            December 4, 2013

            Hi, Your artcile is very good, but I struggle whenever I am asked to develop new Programming codes like if something is given in Socket or Thread…(i.e)different from the usual ones…can you please tell me which books to refer, Can you please give a list of them

            - Brindha

              JournalDev
              DigitalOcean Employee
              DigitalOcean Employee badge
              April 7, 2014

              Hi Pankaj, Thank you for such a beautiful article on one of the most complex topic in java. Can you please elaborate the exact diffrence between shutdown() and shutdownow() api, as the javadoc is not very clear on the shutdownnow()

              - HIMANSU NAYAK

                JournalDev
                DigitalOcean Employee
                DigitalOcean Employee badge
                April 7, 2014

                Hi Pankaj, I have few doubts on this WorkerPool.java example 1. In WorkerPool.java example you have use ThreadFactory interface, but ThreadPoolExecutor uses the defaultThreadFactory() internally in case if it is not passed during creation, then what is the purpose of explicitly doing it in the example? 2. “So if there are 4 running tasks and more tasks are submitted, the work queue will hold only 2 of them and rest of them will be handled by RejectedExecutionHandlerImpl” - if we haven’t implemented RejectionExecutionHandler interface then all the task would be in the queue for the execution without getting rejected?

                - HIMANSU NAYAK

                  JournalDev
                  DigitalOcean Employee
                  DigitalOcean Employee badge
                  April 11, 2014

                  Tq…Pankaj…

                  - Shanmugam

                    JournalDev
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    May 8, 2014

                    Thanks for the great article Pankaj. I had a simple doubt and the first line of the cleared it. You and Jacob Jenkov provide best material for clearing concurrency concepts

                    - Varun

                      Try DigitalOcean for free

                      Click below to sign up and get $200 of credit to try our products over 60 days!

                      Sign up

                      Join the Tech Talk
                      Success! Thank you! Please check your email for further details.

                      Please complete your information!

                      Become a contributor for community

                      Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

                      DigitalOcean Documentation

                      Full documentation for every DigitalOcean product.

                      Resources for startups and SMBs

                      The Wave has everything you need to know about building a business, from raising funding to marketing your product.

                      Get our newsletter

                      Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

                      New accounts only. By submitting your email you agree to our Privacy Policy

                      The developer cloud

                      Scale up as you grow — whether you're running one virtual machine or ten thousand.

                      Get started for free

                      Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

                      *This promotional offer applies to new accounts only.