Why use ThreadFactory

When submitting tasks to java executor service, some thread will be allocated to perform that task. The servicing thread may be assigned from the internal thread pool, or created on-demand. Each executor service has an associated ThreadFactory, and a default ThreadFactory if the application does not specify one. For non-trivial apps, it's always a good idea to set a custom ThreadFactory.

1, To set a more descriptive thread name. With the default ThreadFactory, it gives thread names in the form of pool-m-thread-n, such as pool-1-thread-1, pool-2-thread-1, pool-3-thread-1, etc. When these threads showing up in debugger, profiler, or monitoring tool, it's hard to know their purpose and how they were started.

2, To set thread daemon status. The default ThreadFactory produces non-daemon threads.

3, To set thread contextClassLoader to appropriate value. Threads created by the default ThreadFactory will inherit the contextClassLoader from the parent thread, which may not be what you want. In general, a pooled thread itself should not be associated with any specific class loader. Instead, the submitted task has the best knowledge about its class loading requirement, so the task Runnable or Callable should manage the thread contextClassLoader for the duration of its run method.

If the new thread does not need to access classes loaded in the parent thead, set its contextClassLoader to null. If a task needs visibility to a class loader, set its contextClassLoader at the beginning of its run method, and restore its contextClassLoader right before exiting run method.

The default ThreadFactory in JDK 6 java.util.concurrent is a package-private static nested class in java.util.concurrent.Executors:
static class DefaultThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
final ThreadGroup group;
final AtomicInteger threadNumber = new AtomicInteger(1);
final String namePrefix;

DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null)? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}

public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
Note that poolNumber is declared as static final and threadNumber as final. Each instance of ThreadFactory is tied to a unique pool, and a static poolNumber tracks the sequence of all instances of DefaultThreadFactory. An instance of ThreadFactory (and the associated pool) contains multiple threads, and the instance field threadNumber tallies all threads therein.

Both poolNumber and threadNumber fields are of type java.util.concurrent.AtomicInteger to take advantage of its atomic compound operations. Their initial value is 1 and getAndIncrement() is invoked to produce sequence like 1, 2, 3. Alternatively we could set its initial value to 0 and invoke incrementAndGet() to produce the same sequence.

Why poolNumber and threadNumber need to guard against concurrent access and modification? poolNumber is static and thus a shared data among all instances of DefaultThreadFactory. Multiple instances of DefaultThreadFactory may be incrementing it simutaneously, so it needs to be thread-safe.

As for threadNumber, the creation of threads, either initially at execution service startup time, or under heavy load, may be serialized by the execution service. But an instance of ThreadFactory can be passed around and associated with multiple execution service. Therefore, the internal state of ThreadFactory needs to be thread-safe and immutable.

Followers

Pageviews Last 7 Days