1. Overview

ExecutorService provides a convenient way to manage threads and execute concurrent tasks in Java. When working with ExecutorService, assigning meaningful names to threads and thread pools can be useful to improve debugging, monitoring, and understanding of threads. In this article, we’ll learn about different ways of naming threads and thread pools in Java’s ExecutorService.

First, we’ll see how the default names of threads are set in ExecutorService. Then, we’ll see different ways to customize the thread name using a custom ThreadFactory, BasicThreadFactory of Apache Commons, and ThreadFactoryBuilder of the Guava library.

2. Naming Threads

The thread name can be set in Java easily if we’re not using an ExecutorService. While ExecutorService uses default thread pool and thread names such as “pool-1-thread-1”, “pool-1-thread-2”, etc., specifying a custom thread name for threads managed by ExecutorService is possible.

First, let’s create a simple program to run an ExecuterService. Later, we’ll see how it displays the default thread and thread pool name:

ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
    executorService.execute(() -> System.out.println(Thread.currentThread().getName()));
}

Now, let’s run the program. We can see the default thread name printed:

pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2

2.1. Using a Custom ThreadFactory

In ExecutorService, new threads are created using a ThreadFactory. An ExecutorService uses an Executors.defaultThreadFactory to create its threads to execute tasks.

By supplying a different custom ThreadFactory to the ExecuterService, we can alter the thread’s name, priority, etc.

First, let’s create our own MyThreadFactory which implements ThreadFactory. Then, we’ll create a custom name for any new thread created using our MyThreadFactory:

public class MyThreadFactory implements ThreadFactory {
    private AtomicInteger threadNumber = new AtomicInteger(1);
    private String threadlNamePrefix = "";

    public MyThreadFactory(String threadlNamePrefix) {
        this.threadlNamePrefix = threadlNamePrefix;
    }

    public Thread newThread(Runnable runnable) {
        return new Thread(runnable, threadlNamePrefix + threadNumber.getAndIncrement());
    }
}

Now, we’ll use our custom factory MyThreadFactory to set the thread name and pass it to the ExecutorService:

MyThreadFactory myThreadFactory = new MyThreadFactory("MyCustomThread-");
ExecutorService executorService = Executors.newFixedThreadPool(3, myThreadFactory);
for (int i = 0; i < 5; i++) {
    executorService.execute(() -> System.out.println(Thread.currentThread().getName()));
}

Finally, when we run the program, we can see our custom thread name printed for threads of ExecutorService:

MyCustomThread-1
MyCustomThread-2
MyCustomThread-2
MyCustomThread-3
MyCustomThread-1

2.2. Using BasicThreadFactory From Apache Commons

BasicThreadFactory from commons-lang3 implements the ThreadFactory interface that provides configuration options for the threads, which helps set the thread name.

First, let’s add the commons-lang3 dependency to our project:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

Next, we create the BasicThreadFactory with our custom name. After that, we create the ExecutorService with our factory:

BasicThreadFactory factory = new BasicThreadFactory.Builder()
  .namingPattern("MyCustomThread-%d").priority(Thread.MAX_PRIORITY).build();
ExecutorService executorService = Executors.newFixedThreadPool(3, factory);
for (int i = 0; i < 5; i++) {
    executorService.execute(() -> System.out.println(Thread.currentThread().getName()));
}

Here, we can see the namingPattern() method takes the name pattern for the thread name.

Finally, let’s run the program to see our custom thread name printed:

MyCustomThread-1
MyCustomThread-2
MyCustomThread-2
MyCustomThread-3
MyCustomThread-1

2.3. Using ThreadFactoryBuilder From Guava

ThreadFactoryBuilder from Guava also provides options for customizing the threads it creates.

First, let’s add the guava dependency to our project:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.2.0-jre</version>
</dependency>

Next, we create the ThreadFactory with our custom name, and pass it to the ExecutorService:

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
  .setNameFormat("MyCustomThread-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(3, namedThreadFactory);
for (int i = 0; i < 5; i++) {
    executorService.execute(() -> System.out.println(Thread.currentThread().getName()));
}

Here, we can see setNameFormat() takes the name pattern for the thread name.

Finally, when we run the program, we can see our custom thread names printed:

MyCustomThread-0
MyCustomThread-1
MyCustomThread-2
MyCustomThread-2
MyCustomThread-1

These are some of the ways we can name our threads when working with ExecutorService in Java, offering flexibility based on our application’s requirements.

3. Conclusion

In this article, we’ve learned about different ways of naming threads and thread pools in Java’s ExecutorService.

First, we saw how the default name is set. Later, we customized the thread name using our custom ThreadFactory, and ThreadFactory of different APIs like Apache Commons and Guava.

As always, the example code for this article is available over on GitHub.