为什么需要自定义线程工厂
在开发媒体处理类软件时,经常要并行处理多个视频转码、音频提取或文件上传任务。使用线程池能有效控制资源,但默认的线程创建方式往往不够用。比如,某个后台任务卡住了,你连日志都找不到是哪个线程出的问题,排查起来特别头疼。
这时候,自定义线程工厂就能派上大用场。它允许我们在创建线程时,统一命名规则、设置优先级,甚至加入异常处理逻辑,让整个线程池更透明、更可控。
线程池与ThreadFactory的关系
Java 中的 ThreadPoolExecutor 在创建新线程时,并不直接 new Thread(),而是通过 ThreadFactory 来生成。系统有默认实现,但名字都是 pool-1-thread-1 这种,看不出实际用途。我们完全可以自己写一个工厂,让每个线程的名字带上业务标识,比如 video-encoder-thread-1 或 audio-extractor-2。
动手实现一个带命名规则的线程工厂
下面是一个简单的自定义线程工厂示例,适用于媒体处理场景:
public class NamedThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger threadNumber = new AtomicInteger(1);
public NamedThreadFactory(String taskType) {
this.namePrefix = taskType + "-thread-";
}
@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("video-compress");
ExecutorService executor = new ThreadPoolExecutor(
4,
8,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(100),
factory
);运行后查看日志,线程名会显示为 video-compress-thread-1 这样的格式,一目了然。万一某个线程抛了未捕获异常,也能快速定位到是哪类任务出的问题。
进阶:加入异常捕获和监控
还可以在线程工厂里统一设置未捕获异常处理器,把关键错误上报到监控系统:
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
t.setUncaughtExceptionHandler((thread, ex) -> {
System.err.println("[ERROR] Thread " + thread.getName() + " failed: " + ex.getMessage());
// 可以发送到日志中心或告警系统
});
return t;
}这种做法在长时间运行的媒体服务中特别实用,避免异常静默失败。
实际场景中的好处
想象一下,你的程序同时跑着字幕生成、封面截图、格式转换三个任务,每个都用不同的线程池,而每个线程池都用了对应的自定义线程工厂。当系统负载高时,通过 jstack 或 APM 工具一看,哪些任务占线程、有没有死锁,一眼就能看出来。
比起一堆分不清用途的 thread-1、thread-2,这种管理方式明显更贴近真实运维需求。尤其在调试阶段,节省的时间可不是一点半点。