线程池管理中为什么需要自定义线程工厂
在开发高并发应用时,线程池是提升性能的关键手段。但默认的线程创建方式往往不够用。比如你写了一个后台服务处理用户上传图片,每个任务都交给线程池去执行。时间一长发现日志里全是“Thread-1”“Thread-2”这样的名字,出了问题根本没法快速定位是哪个模块的线程。
这时候就得自己控制线程的创建过程,也就是通过自定义线程工厂来实现更清晰、可控的线程管理。
什么是线程工厂
线程池在创建新线程时,并不直接 new Thread(),而是通过一个叫 ThreadFactory 的接口来生成。这个接口只有一个方法:newThread(Runnable r)。JDK 提供了默认实现,但名字随机、优先级统一,不利于排查问题。
自定义线程工厂就是我们自己实现这个接口,在创建线程时设置有意义的名字、捕获异常、指定优先级甚至绑定上下文信息。
动手写一个自定义线程工厂
比如我们要做一个文件处理系统,希望所有线程名都能体现用途和编号。可以这样写:
public class NamedThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger threadNumber = new AtomicInteger(1);
public NamedThreadFactory(String prefix) {
this.namePrefix = prefix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + "-" + threadNumber.getAndIncrement());
t.setDaemon(false); // 非守护线程
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}然后把这个工厂传给线程池:
ThreadFactory factory = new NamedThreadFactory("file-upload");
ExecutorService executor = new ThreadPoolExecutor(
2,
4,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
factory
);这样一来,日志里出现的线程名称就会是 file-upload-1、file-upload-2,一看就知道是哪个业务模块产生的。
结合实际场景优化线程行为
有时候某个服务特别关键,比如支付回调通知,你希望它的线程能更快响应。可以在工厂里提高优先级:
t.setPriority(Thread.MAX_PRIORITY);或者你想在线程启动前记录一些信息,也可以加日志:
System.out.println("Creating thread: " + t.getName());还可以结合 MDC 实现分布式追踪中的上下文传递,让日志链路更完整。
避免资源泄漏的小技巧
很多开发者忘了关闭线程池,导致应用停不掉。记得在合适时机调用 shutdown():
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}配合自定义工厂使用,整个线程生命周期就变得透明又可控。