Tutorials  /  Linux Basics

Java Thread Pool and ThreadPoolExecutor Tutorial

Ccentron Redaktion · January 2024 ·6 min read ·Linux Basics, Tutorial

Introduction to Java Thread Pool

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. You will learn everything important in this Java Thread Pool and ThreadPoolExecutor Tutorial.

VM

Try it yourself: cloud servers from €3.12/month

ccloud³ VMs from German data centers – deployed in seconds, billed by the hour, with a €200 starting credit for new accounts. Launch a server →

Let’s write a simple program to explain it’s working. First, we need to have a Runnable class, named WorkerThread.java

WorkerThread Class

Go
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.

Go
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.

Code
pool-1-thread-2 Start. Command = 1
...
Finished all threads

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.

Go
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");
    }

}

Monitoring ThreadPoolExecutor

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.

Go
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();
            }
        }
    }
}

ThreadPoolExecutor Implementation Example

Here is the thread pool implementation example using ThreadPoolExecutor.

Go
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(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.

Code
pool-1-thread-1 Start. Command = cmd0
...
[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.

Output Analysis and Conclusion

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.

The output of the above program demonstrates the operation of the thread pool. The pool efficiently manages the submitted tasks, balancing the workload among the available threads. This is evident from the order and completion of tasks as shown in the output.

Advanced ThreadPoolExecutor Features

ThreadPoolExecutor provides much more feature than simple thread pooling. We can specify the number of threads that will be alive when we create ThreadPoolExecutor instance, limit the size of the thread pool, and create our own RejectedExecutionHandler implementation to handle the jobs that can’t fit in the worker queue.

Here is the output of the above program that confirms the above statement:

Code
cmd6 is rejected
...
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false

This section demonstrates the customization and control we have over the thread pooling behavior in Java, particularly using the ThreadPoolExecutor. The ability to reject tasks and monitor the thread pool’s state are essential for building robust concurrent applications.

Conclusion

This tutorial provided an in-depth look at Java's ThreadPoolExecutor and how it can be used to efficiently manage threads in a multi-threaded environment. The examples given illustrate the functionality and flexibility of ThreadPoolExecutor, making it a powerful tool for Java developers.

For further exploration and detailed information, readers are encouraged to delve into the Java documentation and experiment with the thread pool's features, adapting them to their specific needs.

Jetzt 200 € Guthaben sichern

Testen Sie Ihr Setup auf ccloud³

Registrieren Sie sich in der ccloud³ und erhalten Sie 200 € Startguthaben für Ihr Projekt – z. B. für eine PostgreSQL-VM mit automatischen Backups.

centron Redaktion Technische Redaktion

Das Redaktionsteam von centron schreibt Anleitungen aus dem Betriebsalltag: getestet auf unserer eigenen Plattform, betrieben im Rechenzentrum in Hallstadt bei Bamberg.

Kategorie Linux Basics
Teilen
Noch offene Fragen?

Unser Team hilft Ihnen bei Ihrem konkreten Setup weiter – von Menschen, die die Plattform selbst betreiben.

War dieses Tutorial hilfreich?

Ihre Antwort wird anonym gespeichert und hilft uns, die Tutorials zu verbessern.

Kommentare

Noch keine Kommentare – stellen Sie die erste Frage zu diesem Tutorial.

Zum Kommentieren anmelden

Kommentare stehen centron-Kunden offen. Melden Sie sich in Ihrem Konto an, um eine Frage zu diesem Tutorial zu stellen.

Weiterlesen

Das könnte Sie auch interessieren

Jetzt kostenlos anfangen

Melden Sie sich an und erhalten Sie in den ersten 60 Tagen ein Guthaben von 200 € bei centron.

Dieses Werbeangebot gilt nur für neue Konten. Angebot ausschließlich für Gewerbetreibende.

Jetzt loslegen Sales kontaktieren