com.helger.commons.concurrent.BasicThreadFactory Maven / Gradle / Ivy
Show all versions of ph-commons Show documentation
/*
* Copyright (C) 2014-2024 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.helger.commons.concurrent;
import java.util.Locale;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.OverrideOnDemand;
import com.helger.commons.builder.IResettableBuilder;
import com.helger.commons.state.ETriState;
import com.helger.commons.string.ToStringGenerator;
/**
*
* An implementation of the {@code ThreadFactory} interface that provides some
* configuration options for the threads it creates.
*
*
* A {@code ThreadFactory} is used for instance by an {@code ExecutorService} to
* create the threads it uses for executing tasks. In many cases users do not
* have to care about a {@code ThreadFactory} because the default one used by an
* {@code ExecutorService} will do. However, if there are special requirements
* for the threads, a custom {@code ThreadFactory} has to be created.
*
*
* This class provides some frequently needed configuration options for the
* threads it creates. These are the following:
*
*
* - A name pattern for the threads created by this factory can be specified.
* This is often useful if an application uses multiple executor services for
* different purposes. If the names of the threads used by these services have
* meaningful names, log output or exception traces can be much easier to read.
* Naming patterns are format strings as used by the {@code
* String.format()} method. The string can contain the place holder {@code %d}
* which will be replaced by the number of the current thread ({@code
* ThreadFactoryImpl} keeps a counter of the threads it has already created).
* For instance, the naming pattern {@code "My %d. worker thread"} will result
* in thread names like {@code "My 1. worker thread"}, {@code
* "My 2. worker thread"} and so on.
* - A flag whether the threads created by this factory should be daemon
* threads. This can impact the exit behavior of the current Java application
* because the JVM shuts down if there are only daemon threads running.
* - The priority of the thread. Here an integer value can be provided. The
* {@code java.lang.Thread} class defines constants for valid ranges of priority
* values.
* - The {@code UncaughtExceptionHandler} for the thread. This handler is
* called if an uncaught exception occurs within the thread.
*
*
* {@code BasicThreadFactory} wraps another thread factory which actually
* creates new threads. The configuration options are set on the threads created
* by the wrapped thread factory. On construction time the factory to be wrapped
* can be specified. If none is provided, a default {@code ThreadFactory} is
* used.
*
*
* Instances of {@code BasicThreadFactory} are not created directly, but the
* nested {@code Builder} class is used for this purpose. Using the builder only
* the configuration options an application is interested in need to be set. The
* following example shows how a {@code BasicThreadFactory} is created and
* installed in an {@code ExecutorService}:
*
*
*
* // Create a factory that produces daemon threads with a naming pattern and
* // a priority
* BasicThreadFactory factory = new BasicThreadFactory.Builder ().setNamingPattern ("workerthread-%d")
* .setDaemon (true)
* .setPriority (Thread.MAX_PRIORITY)
* .build ();
* // Create an executor service for single-threaded execution
* ExecutorService exec = Executors.newSingleThreadExecutor (factory);
*
*
* @since 8.5.3
*/
public class BasicThreadFactory implements ThreadFactory
{
/**
* The default thread factory
*/
static class ExtendedDefaultThreadFactory implements ThreadFactory
{
private static final AtomicInteger FACTORY_ID = new AtomicInteger (1);
private final ThreadGroup m_aGroup;
private final AtomicInteger m_aThreadNumber = new AtomicInteger (1);
private final String m_sNamePrefix;
ExtendedDefaultThreadFactory (@Nullable final ThreadGroup aTG)
{
final SecurityManager s = System.getSecurityManager ();
m_aGroup = s != null ? s.getThreadGroup () : aTG != null ? aTG : Thread.currentThread ().getThreadGroup ();
m_sNamePrefix = "factory-" + FACTORY_ID.getAndIncrement () + "-thread-";
}
@Nonnull
public Thread newThread (@Nonnull final Runnable r)
{
final Thread t = new Thread (m_aGroup, r, m_sNamePrefix + m_aThreadNumber.getAndIncrement (), 0);
if (t.isDaemon ())
t.setDaemon (false);
if (t.getPriority () != Thread.NORM_PRIORITY)
t.setPriority (Thread.NORM_PRIORITY);
return t;
}
}
private static final Logger LOGGER = LoggerFactory.getLogger (BasicThreadFactory.class);
private static Thread.UncaughtExceptionHandler s_aDefaultUncaughtExceptionHandler = (t, e) -> {
LOGGER.error ("Uncaught exception from Thread " + t.getName (), e);
};
/**
* Set the default uncaught exception handler for future instances of
* BasicThreadFactory. By default a logging exception handler is present.
*
* @param aHdl
* The handlers to be used. May not be null
.
* @since 9.0.0
*/
public static void setDefaultUncaughtExceptionHandler (@Nonnull final Thread.UncaughtExceptionHandler aHdl)
{
ValueEnforcer.notNull (aHdl, "DefaultUncaughtExceptionHandler");
s_aDefaultUncaughtExceptionHandler = aHdl;
}
/**
* @return The default uncaught exception handler used. Never
* null
.
*/
@Nonnull
public static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler ()
{
return s_aDefaultUncaughtExceptionHandler;
}
/** A counter for the threads created by this factory. */
private final AtomicLong m_aThreadCounter;
/** Stores the wrapped factory. */
private final ThreadFactory m_aWrappedFactory;
/** Stores the uncaught exception handler. */
private final Thread.UncaughtExceptionHandler m_aUncaughtExceptionHandler;
/** Stores the naming pattern for newly created threads. */
private final String m_sNamingPattern;
/** Stores the priority. */
private final Integer m_aPriority;
/** Stores the daemon status flag. */
private final ETriState m_eDaemon;
/**
* Creates a new instance of {@code ThreadFactoryImpl} and configures it from
* the specified {@code Builder} object.
*
* @param aBuilder
* the {@code Builder} object
*/
protected BasicThreadFactory (@Nonnull final Builder aBuilder)
{
m_aThreadCounter = new AtomicLong ();
if (aBuilder.m_aWrappedFactory == null)
m_aWrappedFactory = new ExtendedDefaultThreadFactory (aBuilder.m_aThreadGroup);
else
m_aWrappedFactory = aBuilder.m_aWrappedFactory;
m_sNamingPattern = aBuilder.m_sNamingPattern;
m_aPriority = aBuilder.m_nPriority;
m_eDaemon = aBuilder.m_eDaemon;
m_aUncaughtExceptionHandler = aBuilder.m_aUncaughtExceptionHandler;
}
/**
* Returns the wrapped {@code ThreadFactory}. This factory is used for
* actually creating threads. This method never returns null. If no
* {@code ThreadFactory} was passed when this object was created, a default
* thread factory is returned.
*
* @return the wrapped {@code ThreadFactory}
*/
@Nonnull
public final ThreadFactory getWrappedFactory ()
{
return m_aWrappedFactory;
}
/**
* Returns the naming pattern for naming newly created threads. Result can be
* null if no naming pattern was provided.
*
* @return the naming pattern
*/
@Nonnull
public final String getNamingPattern ()
{
return m_sNamingPattern;
}
/**
* Returns the daemon flag. This flag determines whether newly created threads
* should be daemon threads. If true, this factory object calls
* {@code setDaemon(true)} on the newly created threads. Result can be
* null if no daemon flag was provided at creation time.
*
* @return the daemon flag
*/
@Nonnull
public final ETriState getDaemon ()
{
return m_eDaemon;
}
/**
* Returns the priority of the threads created by this factory. Result can be
* null if no priority was specified.
*
* @return the priority for newly created threads
*/
@Nullable
public final Integer getPriority ()
{
return m_aPriority;
}
/**
* Returns the {@code UncaughtExceptionHandler} for the threads created by
* this factory. Result can be null if no handler was provided.
*
* @return the {@code UncaughtExceptionHandler}
*/
public final Thread.UncaughtExceptionHandler getUncaughtExceptionHandler ()
{
return m_aUncaughtExceptionHandler;
}
/**
* Returns the number of threads this factory has already created. This class
* maintains an internal counter that is incremented each time the
* {@link #newThread(Runnable)} method is invoked.
*
* @return the number of threads created by this factory
*/
public long getThreadCount ()
{
return m_aThreadCounter.get ();
}
/**
* Initializes the specified thread. This method is called by
* {@link #newThread(Runnable)} after a new thread has been obtained from the
* wrapped thread factory. It initializes the thread according to the options
* set for this factory.
*
* @param aThread
* the thread to be initialized
*/
@OverrideOnDemand
protected void initializeThread (@Nonnull final Thread aThread)
{
if (m_sNamingPattern != null)
{
final Long aCount = Long.valueOf (m_aThreadCounter.incrementAndGet ());
aThread.setName (String.format ((Locale) null, m_sNamingPattern, aCount));
}
if (m_aUncaughtExceptionHandler != null)
aThread.setUncaughtExceptionHandler (m_aUncaughtExceptionHandler);
if (m_aPriority != null)
aThread.setPriority (m_aPriority.intValue ());
if (m_eDaemon.isDefined ())
aThread.setDaemon (m_eDaemon.getAsBooleanValue ());
}
/**
* Creates a new thread. This implementation delegates to the wrapped factory
* for creating the thread. Then, on the newly created thread the
* corresponding configuration options are set.
*
* @param aRunnable
* the {@code Runnable} to be executed by the new thread
* @return the newly created thread
*/
@Nonnull
public Thread newThread (@Nonnull final Runnable aRunnable)
{
ValueEnforcer.notNull (aRunnable, "Runnable");
final Thread t = getWrappedFactory ().newThread (aRunnable);
initializeThread (t);
return t;
}
@Override
public String toString ()
{
return new ToStringGenerator (this).append ("ThreadCounter", m_aThreadCounter)
.append ("WrappedFactory", m_aWrappedFactory)
.append ("UncaughtExceptionHandler", m_aUncaughtExceptionHandler)
.append ("NamingPattern", m_sNamingPattern)
.append ("Priority", m_aPriority)
.append ("Daemon", m_eDaemon)
.getToString ();
}
@Nonnull
public static Builder builder ()
{
return new Builder ();
}
/**
*
* A builder class for creating instances of {@code
* BasicThreadFactory}.
*
*
* Using this builder class instances of {@code BasicThreadFactory} can be
* created and initialized. The class provides methods that correspond to the
* configuration options supported by {@code BasicThreadFactory}. Method
* chaining is supported. Refer to the documentation of {@code
* BasicThreadFactory} for a usage example.
*
*
* @version $Id: BasicThreadFactory.java 1583482 2014-03-31 22:54:57Z niallp $
*/
public static class Builder implements IResettableBuilder
{
/** The wrapped factory. */
private ThreadFactory m_aWrappedFactory;
/** The ThreadGroup for the default ThreadFactory */
private ThreadGroup m_aThreadGroup;
/** The uncaught exception handler. */
private Thread.UncaughtExceptionHandler m_aUncaughtExceptionHandler = s_aDefaultUncaughtExceptionHandler;
/** The naming pattern. */
private String m_sNamingPattern;
/** The priority. */
private Integer m_nPriority;
/** The daemon flag. */
private ETriState m_eDaemon = ETriState.UNDEFINED;
public Builder ()
{}
/**
* Sets the {@code ThreadFactory} to be wrapped by the new {@code
* BasicThreadFactory}.
*
* @param aWrappedFactory
* the wrapped {@code ThreadFactory} (must not be null)
* @return this for chaining
* @throws NullPointerException
* if the passed in {@code ThreadFactory} is null
*/
@Nonnull
public final Builder wrappedFactory (@Nonnull final ThreadFactory aWrappedFactory)
{
ValueEnforcer.notNull (aWrappedFactory, "Factory");
m_aWrappedFactory = aWrappedFactory;
return this;
}
/**
* Sets the {@code ThreadGroup} to be used by the default thread factory. If
* {@link #wrappedFactory(ThreadFactory)} is used, this setting is useless.
*
* @param aThreadGroup
* the {@code ThreadGroup} to use. May be null
.
* @return this for chaining
*/
@Nonnull
public final Builder threadGroup (@Nullable final ThreadGroup aThreadGroup)
{
m_aThreadGroup = aThreadGroup;
return this;
}
/**
* Sets the uncaught exception handler for the threads created by the new
* {@code BasicThreadFactory}.
*
* @param aExceptionHandler
* the {@code UncaughtExceptionHandler} (must not be null)
* @return this for chaining
* @throws NullPointerException
* if the exception handler is null
*/
@Nonnull
public final Builder uncaughtExceptionHandler (@Nonnull final Thread.UncaughtExceptionHandler aExceptionHandler)
{
ValueEnforcer.notNull (aExceptionHandler, "ExceptionHandler");
m_aUncaughtExceptionHandler = aExceptionHandler;
return this;
}
/**
* Sets the naming pattern to be used by the new {@code
* BasicThreadFactory}. The formatting is done with
* {@link String#format(String, Object...)} using the thread counter (type
* long) as the only parameter.
*
* @param sNamingPattern
* the naming pattern (must not be null)
* @return this for chaining
* @throws NullPointerException
* if the naming pattern is null
*/
@Nonnull
public final Builder namingPattern (@Nonnull final String sNamingPattern)
{
ValueEnforcer.notNull (sNamingPattern, "NamingPattern");
m_sNamingPattern = sNamingPattern;
return this;
}
/**
* Sets the priority for the threads created by the new {@code
* BasicThreadFactory}.
*
* @param nPriority
* the priority
* @return this for chaining
*/
@Nonnull
public final Builder priority (final int nPriority)
{
m_nPriority = Integer.valueOf (nPriority);
return this;
}
/**
* Sets the daemon flag for the new {@code BasicThreadFactory}. If this flag
* is set to true the new thread factory will create daemon threads.
*
* @param bDaemon
* the value of the daemon flag
* @return this for chaining
*/
@Nonnull
public final Builder daemon (final boolean bDaemon)
{
m_eDaemon = ETriState.valueOf (bDaemon);
return this;
}
/**
* Resets this builder. All configuration options are set to default values.
* Note: If the {@link #build()} method was called, it is not necessary to
* call {@code reset()} explicitly because this is done automatically.
*/
public void reset ()
{
m_aWrappedFactory = null;
m_aThreadGroup = null;
m_aUncaughtExceptionHandler = null;
m_sNamingPattern = null;
m_nPriority = null;
m_eDaemon = null;
}
/**
* Creates a new {@code BasicThreadFactory} with all configuration options
* that have been specified by calling methods on this builder. After
* creating the factory {@link #reset()} is called.
*
* @return the new {@code BasicThreadFactory}
*/
@Nonnull
public BasicThreadFactory build ()
{
return new BasicThreadFactory (this);
}
}
}