为什么 ThreadPoolExecutor 依赖工厂模式?这篇文章告诉你

207 阅读5分钟

工厂模式:线程池中的设计智慧

在软件设计领域,工厂模式(Factory Pattern) 是一种经典的创建型设计模式。它通过定义一个创建对象的接口,将具体对象的创建过程封装起来,从而将对象的创建与使用解耦,极大地提高了代码的灵活性和可扩展性

工厂模式的核心思想是:将实例化对象的逻辑封装到工厂类中,而不是在代码中直接使用 new 关键字创建对象。这样,开发者在需要创建新对象时,只需要调用工厂方法,而不需要关心对象的具体实现。这不仅提升了代码的可维护性,也使得程序能够更容易适应未来的扩展需求。

工厂模式在 Java 线程池中的应用

在 Java 生态中,线程池(Thread Pool) 是并发编程的基石,它通过复用线程资源来避免频繁创建和销毁线程,从而减少性能开销,提高系统的并发能力。而 ThreadPoolExecutor 作为 Java 并发包 java.util.concurrent 中最核心的线程池实现,正是工厂模式应用的典型案例

ThreadPoolExecutor 的设计中,线程的创建是由 ThreadFactory 工厂接口 负责的,而非直接在 ThreadPoolExecutor 内部 new Thread() 生成。这种设计解耦了线程池与具体线程的创建逻辑,使得开发者可以通过自定义 ThreadFactory 来控制线程的创建行为。

ThreadFactory:线程池的“造线程工厂”

ThreadFactory 是 Java 提供的一个 函数式接口,其核心方法如下:

public interface ThreadFactory {
    Thread newThread(Runnable r);
}

这个接口的作用就是为线程池提供统一的线程创建机制,默认实现是 Executors.defaultThreadFactory(),但在很多实际场景中,我们需要自定义 ThreadFactory,比如:

  • 为线程设置更高的优先级(如高并发任务)
  • 给线程赋予有意义的名称(方便日志追踪和排查问题)
  • 创建守护线程(在 JVM 退出时自动关闭)
  • 对线程进行异常捕获(防止线程意外终止)

自定义 ThreadFactory 的优势

默认情况下,ThreadPoolExecutor 使用的线程工厂较为简单,创建的线程没有自定义名称、默认优先级、非守护线程。然而,在高并发业务场景下,这种默认实现往往不能满足需求。因此,我们可以通过自定义 ThreadFactory,实现更灵活、更符合业务需求的线程管理。

1. 设置线程优先级

线程优先级(Thread Priority)决定了线程在 CPU 调度中的执行权重。虽然 Java 的线程优先级不能完全控制线程的执行顺序,但合理的设置有助于优化性能,例如:

  • 实时任务(如金融交易系统)可设置较高优先级
  • 后台任务(如日志收集)可设置较低优先级

2. 自定义线程名称

在生产环境中,日志是排查问题的关键。默认线程名称往往缺乏可读性,而自定义线程名称后,可以直接在日志中定位到问题线程,极大提升运维效率。例如:

Thread thread = new Thread(r, "MyPool-Thread-" + threadNumber.getAndIncrement());

这样,日志中会显示 MyPool-Thread-1,而不是 Thread-0,直观且易查。

3. 指定线程类型(用户线程 vs 守护线程)

  • 用户线程(User Thread):普通线程,需要手动管理生命周期,程序不会因其结束而终止。
  • 守护线程(Daemon Thread):后台线程,JVM 退出时自动回收,适用于日志收集、监控任务等

示例代码:自定义 ThreadFactory

以下代码实现了一个可配置的 ThreadFactory,能够自定义线程名称、优先级、守护模式

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class CustomThreadFactory implements ThreadFactory {
    private final String threadNamePrefix; // 线程名称前缀
    private final int threadPriority; // 线程优先级
    private final boolean daemon; // 是否为守护线程
    private final AtomicInteger threadNumber = new AtomicInteger(1); // 线程编号

    public CustomThreadFactory(String threadNamePrefix, int threadPriority, boolean daemon) {
        this.threadNamePrefix = threadNamePrefix;
        this.threadPriority = threadPriority;
        this.daemon = daemon;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r, threadNamePrefix + "-" + threadNumber.getAndIncrement());
        thread.setPriority(threadPriority); // 设置线程优先级
        thread.setDaemon(daemon); // 设置线程类型
        return thread;
    }
}

如何使用自定义 ThreadFactory

我们可以在创建 ThreadPoolExecutor 时传入 CustomThreadFactory,这样线程池中的线程就会按照我们定义的方式创建:

import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建线程池,并使用自定义 ThreadFactory
        ExecutorService executor = new ThreadPoolExecutor(
            2, 4, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            new CustomThreadFactory("Worker", Thread.NORM_PRIORITY, false)
        );

        // 提交任务
        for (int i = 0; i < 5; i++) {
            executor.execute(() -> System.out.println(Thread.currentThread().getName() + " 正在执行任务"));
        }

        // 关闭线程池
        executor.shutdown();
    }
}

示例运行结果(线程名称可见):

Worker-1 正在执行任务
Worker-2 正在执行任务
Worker-3 正在执行任务
Worker-4 正在执行任务
Worker-5 正在执行任务

通过 CustomThreadFactory,我们可以清晰看到线程池中的线程名称,便于管理和调试。


总结

工厂模式在 ThreadPoolExecutor 的应用,是设计模式在实际开发中的经典案例。它不仅增强了线程创建的灵活性,还提升了代码的可维护性。通过自定义 ThreadFactory,我们可以:

  1. 提升线程管理的灵活性(如控制线程优先级)
  2. 优化程序可读性(通过自定义线程名称更易排查问题)
  3. 提高应用的稳定性(设置守护线程、异常捕获等)

在实际开发中,我们应该深刻理解设计模式的价值,并在合适的场景下灵活运用,从而提升代码质量和系统性能。