在项目中使用JAVA线程池,日志打印的线程名为
pool-1-thread-1
格式,我们无法准确定位到是什么业务在使用线程。而线程池中线程的创建,由ThreadFactory
接口来实现。那么我们自定义线程工厂类便可以解决该问题。
1. 测试代码
@Slf4j
public class ThreadTest {
public static void main(String[] args) {
/**
* 使用Spring提供的ThreadFactory
*/
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 100
, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new CustomizableThreadFactory("demo-"));
executor.execute(() -> {
log.info("CustomizableThreadFactory数据...");
});
ThreadPoolExecutor executor2 = new ThreadPoolExecutor(1, 1, 100
, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor2.execute(() -> {
log.info("executor2数据...");
});
ThreadPoolExecutor executor3 = new ThreadPoolExecutor(1, 1, 100
, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor3.execute(() -> {
log.info("executor3数据...");
});
}
}
输出日志
11:31:32.010 [pool-1-thread-1] INFO com.tellme.demo.ThreadTest - executor2数据...
11:31:32.010 [demo-1] INFO com.tellme.demo.ThreadTest - CustomizableThreadFactory数据...
11:38:17.462 [pool-2-thread-1] INFO com.tellme.demo.ThreadTest - executor3数据...
可以看到,当使用JAVA默认的线程池工厂类创建线程池时,pool-2...
是递增的。
2. 默认的ThreadFactory
/**
* The default thread factory
*/
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private 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;
}
}
注意:poolNumber
是static变量,即全局递增。threadNumber
为常量,每个对象递增。
我们重实现ThreadFactory,可以将线程池前缀传入。
3. 自定义的ThreadFactory
源码位置:com.alibaba.dubbo.common.utils.NamedThreadFactory
public class NamedThreadFactory implements ThreadFactory {
protected static final AtomicInteger POOL_SEQ = new AtomicInteger(1);
protected final AtomicInteger mThreadNum = new AtomicInteger(1);
protected final String mPrefix;
protected final boolean mDaemon;
protected final ThreadGroup mGroup;
public NamedThreadFactory() {
this("pool-" + POOL_SEQ.getAndIncrement(), false);
}
public NamedThreadFactory(String prefix) {
this(prefix, false);
}
public NamedThreadFactory(String prefix, boolean daemon) {
mPrefix = prefix + "-thread-";
mDaemon = daemon;
SecurityManager s = System.getSecurityManager();
mGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
}
@Override
public Thread newThread(Runnable runnable) {
String name = mPrefix + mThreadNum.getAndIncrement();
Thread ret = new Thread(mGroup, runnable, name, 0);
ret.setDaemon(mDaemon);
return ret;
}
public ThreadGroup getThreadGroup() {
return mGroup;
}
}
便可以传入线程池名称和决定是否为守护线程。
4. ThreadFactory深入理解
- 线程池是重复理利用线程资源,所以ThreadFactory的
newThread
方法是第一次创建线程时才会被调用; - 调用
java.util.concurrent.ThreadFactory#newThread
时创建子线程,是主线程执行的。
比较复杂的ThreadLocal详看: