Explain Codes LogoExplain Codes Logo

Naming threads and thread-pools of ExecutorService

java
thread-pool
executor-service
thread-factory
Anton ShumikhinbyAnton Shumikhin·Jan 26, 2025
TLDR

Assign names to threads within an ExecutorService using a custom ThreadFactory. This factory acts like a fancy name tag dispenser for your threads:

ThreadFactory namedFactory = new ThreadFactory() { private final AtomicInteger threadNumber = new AtomicInteger(0); public Thread newThread(Runnable r) { return new Thread(r, "PoolThread-" + threadNumber.incrementAndGet()); } }; ExecutorService executor = Executors.newFixedThreadPool(5, namedFactory);

Your threads now sound like a boyband: "PoolThread-1", "PoolThread-2", and so on. Each has a stage name, unique, and ready to rock.

The Guava and Apache way

The ThreadFactoryBuilder (a Guava's gift)

Elegance and simplicity get a new definition with Guava's ThreadFactoryBuilder:

ThreadFactory namedFactory = new ThreadFactoryBuilder() .setNameFormat("Worker-%d") // because all workers dream of being a unique snowflake .build(); ExecutorService service = Executors.newFixedThreadPool(10, namedFactory);

The readability boost you get from naming your threads is invaluable for both debugging and monitoring. It's now less like finding a needle in a haystack and more like a scavenger hunt.

BasicThreadFactory of Apache Commons-Lang

In the versatile Apache's toolbox, we find the BasicThreadFactory:

ThreadFactory customThreadFactory = new BasicThreadFactory.Builder() .namingPattern("CustomPool-%d") // because 'Thread-0' sounds like 'John Doe' .daemon(true) // because having an exit strategy is always smart .priority(Thread.MAX_PRIORITY) // because we always prioritize our tasks, right? .build(); ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(customThreadFactory);

Apache makes configuration options, like setting the daemon status and thread priority, simpler than swapping the coffee filter.

Kotlin and Lambda expressions

ThreadFactory can take a concise and expressive form in Kotlin using lambda expressions:

val executor = Executors.newFixedThreadPool(5) { runnable -> Thread(runnable).apply { name = "KotlinWorker-${someRandomIdGenerator()}" // behold, the Kotlin magic! } }

The nitty-gritty of thread naming

Putting names to single and scheduled threads

Who says only thread pools can have nice things? Single thread executors can have custom thread names too:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor( new ThreadFactoryBuilder().setNameFormat("Worker-%d-on-a-mission").build() ); ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor( new ThreadFactoryBuilder().setNameFormat("Scheduler-%d-running-against-clock").build() );

A customised approach brings navigability even when handling different types of executors.

Reusable ThreadFactories to save the day

Make your ThreadFactory swappable and reusable:

public class NamedThreadFactory implements ThreadFactory { private final String baseName; private final AtomicInteger threadNum = new AtomicInteger(1); public NamedThreadFactory(String baseName) { this.baseName = baseName; } @Override public Thread newThread(Runnable r) { return new Thread(r, baseName + "-" + threadNum.getAndIncrement()); // assembling a Transformer, one piece at a time. } } // Usage: ExecutorService pool = Executors.newCachedThreadPool(new NamedThreadFactory("SuperWorkers"));

Knowing when to use custom thread naming

Custom thread naming shines in scenarios like:

  • Identifying CPU hogs: Which threads are the schoolyard bullies?
  • Deadlock discos or thread leak mysteries: Who's not playing nice?
  • Multiple pools in a complex system: Who's swimming where?

Thread names should be distinctive, informative and adhere to a naming convention that doesn't require a Rosetta Stone.