package org.multiverse.utils;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A customizable implementation of the {@link java.util.concurrent.ThreadFactory}. The new
* java.util.concurrency library provides a {@link java.util.concurrent.ThreadFactory} interface, which is a great
* thing, but strangely enough it doesn't provide an customizable implementation.
* <p/>
* If the maximum priority of the ThreadGroup is changed after this StandardThreadFactory is
* constructed, then this will be ignored by the StandardThreadFactory. So it could be that a
* StandardThreadFactory has a higher priority than the ThreadGroup allowed. What will happen at
* construction?
*
* @author Peter Veentjer.
*/
@SuppressWarnings({"ClassWithTooManyConstructors"})
public final class StandardThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private static String createThreadGroupName() {
return Integer.toString(poolNumber.getAndIncrement());
}
private final ThreadGroup threadGroup;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
private final boolean daemon;
private volatile int priority;
/**
* Constructs a new StandardThreadFactory with a Thread.NORM_PRIORITY as priority and a newly
* created ThreadGroup. The created Threads are not daemons.
*/
public StandardThreadFactory() {
this(Thread.NORM_PRIORITY, createThreadGroupName());
}
/**
* Constructs a new StandardThreadFactory with a Thread.NORM_PRIORITY as priority and with a
* newly created ThreadGroup with the given groupName. The created threads are not daemons.
*
* @param groupName the name of the ThreadGroup (is allowed to be null).
*/
public StandardThreadFactory(String groupName) {
this(Thread.NORM_PRIORITY, groupName);
}
/**
* Constructs a new StandardThreadFactory with the given priority. The created threads are
* not daemons.
*
* @param priority the priority of th threads.
* @throws IllegalArgumentException if the priority is not valid.
*/
public StandardThreadFactory(int priority) {
this(priority, createThreadGroupName());
}
/**
* Constructs a new StandardThreadFactory with the given priority and with a newly created
* ThreadGroup with the given groupName. The created threads are not daemons.
*
* @param priority the priority of the threads this StandardThreadFactory is going to createReference.
* @param groupName the name of the ThreadGroup (is allowed to be null).
* @throws IllegalArgumentException if priority is not a valid value.
*/
public StandardThreadFactory(int priority, String groupName) {
this(priority, new ThreadGroup(Thread.currentThread().getThreadGroup(), groupName), false);
}
/**
* Constructs a new StandardThreadFactory with the given priority and are part of the give
* ThreadGroup. The created threads are not daemons.
*
* @param priority the priority of the created threads.
* @param threadGroup the ThreadGroup the created Threads are part of.
* @throws NullPointerException if threadGroup is null
* @throws IllegalArgumentException if the priority is not valid value.
*/
public StandardThreadFactory(int priority, ThreadGroup threadGroup) {
this(priority, threadGroup, false);
}
/**
* Creates a new StandardThreadFactory with the given priority and if the threads are daemons
*
* @param priority the priority of the thread.
* @param daemon if the thread is a daemon.
*/
public StandardThreadFactory(int priority, boolean daemon) {
this(priority, new ThreadGroup(
Thread.currentThread().getThreadGroup(),
createThreadGroupName()),
daemon);
}
/**
* Constructs a new StandardThreadFactory with the given priority and ThreadGroup.
*
* @param priority the priority of the threads this StandardThreadFactory is going to createReference.
* @param threadGroup the ThreadGroup the thread is part of
* @param daemon if the thread should be a daemon.
* @throws IllegalArgumentException if the priority is not valid.
* @throws NullPointerException if threadGroup is null.
*/
public StandardThreadFactory(int priority, ThreadGroup threadGroup, boolean daemon) {
if (threadGroup == null) {
throw new NullPointerException();
}
this.threadGroup = threadGroup;
ensureValidPriority(priority);
this.priority = priority;
this.daemon = daemon;
this.namePrefix = threadGroup.getName() + "-thread#";
}
private void ensureValidPriority(int priority) {
if (priority < Thread.MIN_PRIORITY) {
throw new IllegalArgumentException("priority can`t be smaller than: " +
Thread.MIN_PRIORITY + ", priority was: " + priority);
}
if (priority > Thread.MAX_PRIORITY) {
throw new IllegalArgumentException("priority can`t be greater than: " +
Thread.MAX_PRIORITY + ", priority was: " + priority);
}
if (priority > threadGroup.getMaxPriority()) {
throw new IllegalArgumentException(
"priority can`t be greater than threadGroup.maxPriority: " +
threadGroup.getMaxPriority() + ", priority was: " + priority);
}
}
/**
* Returns true if this StandardThreadFactory is producing daemon threads, false
* otherwise.
*
* @return true if this StandardThreadFactory is producing daemon threads, false
* otherwise.
*/
public boolean isProducingDaemons() {
return daemon;
}
/**
* Returns the ThreadGroup of the created Threads.
*
* @return the ThreadGroup of the created Threads.
*/
public ThreadGroup getThreadGroup() {
return threadGroup;
}
/**
* Returns the priority of created Threads. This is a value ranging from
* Thread.MIN_PRIORITY to Thread.MAX_PRIORITY.
*
* @return the priority of created Threads.
*/
public int getPriority() {
return priority;
}
/**
* Sets the priority of the threads. This will only effect newly created Threads. A value must
* be getAndSet ranging from Thread.MIN_PRIORITY and Thread.MAX_PRIORITY.
* <p/>
* This call is not completely threadsafe, the following scenario could happen:
* <ol>
* <li>thread1 call setPriority and newTransaction the checking part of this method and the check passes</li>
* <li>thread2 calls the ThreadGroup directly and lowers the priority</li>
* <li>thread1 sets the priority on this StandardThreadFactory</li>
* </ol>
* The consequence is that the priority of this StandardThreadFactory is higher than the maximum
* priority of the ThreadGroup and this means that thread creation could fail because threads are
* created with a too high priority. This race problem is very hard to prevent because the check/getAndSet
* can't be done atomically because the ThreadGroup is exposed.
*
* @param priority the new priority.
* @throws IllegalArgumentException if priority is smaller than {@link Thread#MIN_PRIORITY} or
* larger than {@link Thread#MAX_PRIORITY} or larger than the
* maximum priority of the ThreadGroup.
*/
public void setPriority(int priority) {
ensureValidPriority(priority);
this.priority = priority;
}
@Override
public Thread newThread(Runnable runnable) {
if (runnable == null) throw new NullPointerException();
String threadName = namePrefix + threadNumber.getAndIncrement();
Thread thread = new Thread(threadGroup, runnable, threadName);
thread.setDaemon(daemon);
thread.setPriority(priority);
return thread;
}
}