拒绝硬编码:如何用设计模式优化动态线程池

19 阅读14分钟

最近一段时间完成了一个简单的动态线程池的项目 有兴趣的读者可以去这两篇文章查看 juejin.cn/post/759586…

juejin.cn/post/759724…

在开发过程中,我主要参考了 Hippo4j 的设计思路。我发现,很多看似复杂的动态配置功能,其实都是通过经典的设计模式解耦实现的。今天这篇通过复盘,我想把其中用到的核心设计模式单独拎出来,和大家聊聊它们是如何让线程池‘动’起来的

一.Builder设计模式

建造者设计模式绝对是我们日常开发中最常用的设计模式 我认为它和单例模式不相上下 我们都知道设计模式的最终目标都是设计出便于扩展的代码 解决构建灵活性与可维护性问题 在谈起 Builder设计模式 之前 先来讲一下没有构建者模式会出现什么问题

假设我们现在有一个动态线程池基类 它继承自ThreadPoolExecutor 扩展了一些简单的功能 比如说 线程Id

随着线程池功能逐步增强,我们需要引入更多自定义参数,例如:动态线程池标识、项目关闭时等待任务完成的最大时长等 但是我们发现 我们每次引入一个新的参数 就需要修改大量的代码 创建大量的构造器 这肯定是不利于扩展的 这时候就需要Builder设计模式了 在日常开发中 我们如果想使用Builder模式创建一个对象 只需要使用Lombok@Builder注解就可以了

public DynamicThreadPool(String threadPoolId, int coreSize, int maxSize, 
                         long keepAlive, TimeUnit unit, boolean waitForTasks, 
                         long awaitSeconds, RejectedExecutionHandler handler) {
    // ...赋值逻辑
}

// 调用时是一场噩梦:
new DynamicThreadPool("user-pool", 10, 20, 60, TimeUnit.SECONDS, true, 30, new AbortPolicy());

这是构建者模式

DynamicThreadPool pool = DynamicThreadPool.builder()
    .threadPoolId("user-pool")
    .corePoolSize(10)
    .maxPoolSize(20)
    .waitForTasksToCompleteOnShutdown(true) // 新增参数随加随用
    .awaitTerminationSeconds(30)
    .build();

但动态线程池需要对其中的一些核心参数进行校验 比如说线程池容量是否匹配 拒绝策略是否合法 等待时间是否超出我们的阈值 所以我们需要手写一个自己的Builder模式 来实现核心参数的校验 这里我提取了Hippo4j 的源码 建议想要学习这方面的读者可以去github.com/opengoofy/h… 下载源码自行观看:

private static AbstractBuildThreadPoolTemplate.ThreadPoolInitParam buildInitParam(ThreadPoolBuilder builder) {
    AbstractBuildThreadPoolTemplate.ThreadPoolInitParam initParam;
    if (builder.threadFactory == null) {
        Assert.notEmpty(builder.threadNamePrefix, "The thread name prefix cannot be empty or an empty string.");
        initParam = new AbstractBuildThreadPoolTemplate.ThreadPoolInitParam(builder.threadNamePrefix, builder.isDaemon);
    } else {
        initParam = new AbstractBuildThreadPoolTemplate.ThreadPoolInitParam(builder.threadFactory);
    }
    initParam.setCorePoolNum(builder.corePoolSize)
            .setMaximumPoolSize(builder.maximumPoolSize)
            .setKeepAliveTime(builder.keepAliveTime)
            .setCapacity(builder.capacity)
            .setExecuteTimeOut(builder.executeTimeOut)
            .setRejectedExecutionHandler(builder.rejectedExecutionHandler)
            .setTimeUnit(builder.timeUnit)
            .setAllowCoreThreadTimeOut(builder.allowCoreThreadTimeOut)
            .setTaskDecorator(builder.taskDecorator);
    if (builder.isDynamicPool) {
        String threadPoolId = Optional.ofNullable(builder.threadPoolId).orElse(builder.threadNamePrefix);
        initParam.setThreadPoolId(threadPoolId);
        initParam.setWaitForTasksToCompleteOnShutdown(builder.waitForTasksToCompleteOnShutdown);
        initParam.setAwaitTerminationMillis(builder.awaitTerminationMillis);
    }
    if (!builder.isFastPool) {
        if (builder.workQueue == null) {
            if (builder.blockingQueueType == null) {
                builder.blockingQueueType = BlockingQueueTypeEnum.LINKED_BLOCKING_QUEUE;
            }
            builder.workQueue = BlockingQueueTypeEnum.createBlockingQueue(builder.blockingQueueType.getType(), builder.capacity);
        }
        initParam.setWorkQueue(builder.workQueue);
    }
    return initParam;
}

@Override
public ThreadPoolExecutor build() {
    return isDynamicPool ? buildDynamicPool(this) : buildPool(this);
}

可以看到 其builder对核心参数进行了校验 这就是我们自己手写builder设计模式的好处

二.简单工厂模式

简而言之,就是把“创建对象”的过程封装到一个单独的类(工厂)中,让调用者不需要关心具体的创建逻辑。

在没有工厂模式时,如果我们需要一个对象,通常直接使用 new 关键字。 而在简单工厂模式中,我们将对象的创建权利交给一个“工厂类”。客户端只需要告诉工厂“我想要什么(参数)”,工厂就会返回对应的对象。

可以看一下实战代码:

CALLER_RUNS_POLICY(1, "CallerRunsPolicy", new ThreadPoolExecutor.CallerRunsPolicy()),

ABORT_POLICY(2, "AbortPolicy", new ThreadPoolExecutor.AbortPolicy()),

DISCARD_POLICY(3, "DiscardPolicy", new ThreadPoolExecutor.DiscardPolicy()),

DISCARD_OLDEST_POLICY(4, "DiscardOldestPolicy", new ThreadPoolExecutor.DiscardOldestPolicy()),

RUNS_OLDEST_TASK_POLICY(5, "RunsOldestTaskPolicy", new RunsOldestTaskPolicy()),

SYNC_PUT_QUEUE_POLICY(6, "SyncPutQueuePolicy", new SyncPutQueuePolicy());

@Getter
private Integer type;

@Getter
private String name;

private RejectedExecutionHandler rejectedHandler;

RejectedPolicyTypeEnum(Integer type, String name, RejectedExecutionHandler rejectedHandler) {
    this.type = type;
    this.name = name;
    this.rejectedHandler = rejectedHandler;
}

static {
    ServiceLoaderRegistry.register(CustomRejectedExecutionHandler.class);
}

public static RejectedExecutionHandler createPolicy(String name) {
    RejectedPolicyTypeEnum rejectedTypeEnum = Stream.of(RejectedPolicyTypeEnum.values())
            .filter(each -> Objects.equals(each.name, name))
            .findFirst()
            .orElse(null);
    if (rejectedTypeEnum != null) {
        return rejectedTypeEnum.rejectedHandler;
    }
    Collection<CustomRejectedExecutionHandler> customRejectedExecutionHandlers = ServiceLoaderRegistry
            .getSingletonServiceInstances(CustomRejectedExecutionHandler.class);
    Optional<RejectedExecutionHandler> customRejected = customRejectedExecutionHandlers.stream()
            .filter(each -> Objects.equals(name, each.getName()))
            .map(each -> each.generateRejected())
            .findFirst();
    return customRejected.orElse(ABORT_POLICY.rejectedHandler);
}

public static RejectedExecutionHandler createPolicy(int type) {
    Optional<RejectedExecutionHandler> rejectedTypeEnum = Stream.of(RejectedPolicyTypeEnum.values())
            .filter(each -> Objects.equals(type, each.type))
            .map(each -> each.rejectedHandler)
            .findFirst();
    RejectedExecutionHandler resultRejected = rejectedTypeEnum.orElseGet(() -> {
        Collection<CustomRejectedExecutionHandler> customRejectedExecutionHandlers = ServiceLoaderRegistry
                .getSingletonServiceInstances(CustomRejectedExecutionHandler.class);
        Optional<RejectedExecutionHandler> customRejected = customRejectedExecutionHandlers.stream()
                .filter(each -> Objects.equals(type, each.getType()))
                .map(each -> each.generateRejected())
                .findFirst();
        return customRejected.orElse(ABORT_POLICY.rejectedHandler);
    });
    return resultRejected;
}

public static String getRejectedNameByType(int type) {
    return createPolicy(type).getClass().getSimpleName();
}

public static RejectedPolicyTypeEnum getRejectedPolicyTypeEnumByName(String name) {
    Optional<RejectedPolicyTypeEnum> rejectedTypeEnum = Stream.of(RejectedPolicyTypeEnum.values())
            .filter(each -> each.name.equals(name))
            .findFirst();
    return rejectedTypeEnum.orElse(ABORT_POLICY);
}

当你向这个工厂类传入一个拒绝策略名称或者类型的时候 它会在内部校验是否合法 然后返回一个拒绝策略类 假设我们不使用这个工厂 就得在业务层写逻辑校验 而且每当你添加一个拒绝策略的时候 大量涉及到逻辑校验的地方都需要修改

同样的还有阻塞队列的设计 前文已经提到过 我们需要自己设计阻塞队列所以工厂模式也是必不可少的

/**
 * {@link java.util.concurrent.ArrayBlockingQueue}
 */
ARRAY_BLOCKING_QUEUE(1, "ArrayBlockingQueue") {

    @Override
    <T> BlockingQueue<T> of(Integer capacity) {
        return new ArrayBlockingQueue<>(capacity);
    }

    @Override
    <T> BlockingQueue<T> of() {
        return new ArrayBlockingQueue<>(DEFAULT_CAPACITY);
    }
},

/**
 * {@link java.util.concurrent.LinkedBlockingQueue}
 */
LINKED_BLOCKING_QUEUE(2, "LinkedBlockingQueue") {

    @Override
    <T> BlockingQueue<T> of(Integer capacity) {
        return new LinkedBlockingQueue<>(capacity);
    }

    @Override
    <T> BlockingQueue<T> of() {
        return new LinkedBlockingQueue<>();
    }
},

/**
 * {@link java.util.concurrent.LinkedBlockingDeque}
 */
LINKED_BLOCKING_DEQUE(3, "LinkedBlockingDeque") {

    @Override
    <T> BlockingQueue<T> of(Integer capacity) {
        return new LinkedBlockingDeque<>(capacity);
    }

    @Override
    <T> BlockingQueue<T> of() {
        return new LinkedBlockingDeque<>();
    }
},

/**
 * {@link java.util.concurrent.SynchronousQueue}
 */
SYNCHRONOUS_QUEUE(4, "SynchronousQueue") {

    @Override
    <T> BlockingQueue<T> of(Integer capacity) {
        return new SynchronousQueue<>();
    }

    @Override
    <T> BlockingQueue<T> of() {
        return new SynchronousQueue<>();
    }
},

/**
 * {@link java.util.concurrent.LinkedTransferQueue}
 */
LINKED_TRANSFER_QUEUE(5, "LinkedTransferQueue") {

    @Override
    <T> BlockingQueue<T> of(Integer capacity) {
        return new LinkedTransferQueue<>();
    }

    @Override
    <T> BlockingQueue<T> of() {
        return new LinkedTransferQueue<>();
    }
},

/**
 * {@link java.util.concurrent.PriorityBlockingQueue}
 */
PRIORITY_BLOCKING_QUEUE(6, "PriorityBlockingQueue") {

    @Override
    <T> BlockingQueue<T> of(Integer capacity) {
        return new PriorityBlockingQueue<>(capacity);
    }

    @Override
    <T> BlockingQueue<T> of() {
        return new PriorityBlockingQueue<>();
    }
},

/**
 * {@link ResizableCapacityLinkedBlockingQueue}
 */
RESIZABLE_LINKED_BLOCKING_QUEUE(9, "ResizableCapacityLinkedBlockingQueue") {

    @Override
    <T> BlockingQueue<T> of(Integer capacity) {
        return new ResizableCapacityLinkedBlockingQueue<>(capacity);
    }

    @Override
    <T> BlockingQueue<T> of() {
        return new ResizableCapacityLinkedBlockingQueue<>();
    }
};

@Getter
private final Integer type;

@Getter
private final String name;

/**
 * Create the specified implement of BlockingQueue with init capacity.
 * Abstract method, depends on sub override
 *
 * @param capacity the capacity of the queue
 * @param <T>      the class of the objects in the BlockingQueue
 * @return a BlockingQueue view of the specified T
 */
abstract <T> BlockingQueue<T> of(Integer capacity);

/**
 * Create the specified implement of BlockingQueue,has no capacity limit.
 * Abstract method, depends on sub override
 *
 * @param <T> the class of the objects in the BlockingQueue
 * @return a BlockingQueue view of the specified T
 */
abstract <T> BlockingQueue<T> of();

BlockingQueueTypeEnum(int type, String name) {
    this.type = type;
    this.name = name;
}

private static final Map<Integer, BlockingQueueTypeEnum> TYPE_TO_ENUM_MAP;
private static final Map<String, BlockingQueueTypeEnum> NAME_TO_ENUM_MAP;

static {
    final BlockingQueueTypeEnum[] values = BlockingQueueTypeEnum.values();
    TYPE_TO_ENUM_MAP = new HashMap<>(values.length);
    NAME_TO_ENUM_MAP = new HashMap<>(values.length);
    for (BlockingQueueTypeEnum value : values) {
        TYPE_TO_ENUM_MAP.put(value.type, value);
        NAME_TO_ENUM_MAP.put(value.name, value);
    }
}

/**
 * Creates a BlockingQueue with the given {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name}
 * and capacity.
 *
 * @param blockingQueueName {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name}
 * @param capacity          the capacity of the BlockingQueue
 * @param <T>               the class of the objects in the BlockingQueue
 * @return a BlockingQueue view of the specified T
 */
private static <T> BlockingQueue<T> of(String blockingQueueName, Integer capacity) {
    final BlockingQueueTypeEnum typeEnum = NAME_TO_ENUM_MAP.get(blockingQueueName);
    if (typeEnum == null) {
        return null;
    }
    return Objects.isNull(capacity) ? typeEnum.of() : typeEnum.of(capacity);
}

/**
 * Creates a BlockingQueue with the given {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
 * and capacity.
 *
 * @param type     {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
 * @param capacity the capacity of the BlockingQueue
 * @param <T>      the class of the objects in the BlockingQueue
 * @return a BlockingQueue view of the specified T
 */
private static <T> BlockingQueue<T> of(int type, Integer capacity) {
    final BlockingQueueTypeEnum typeEnum = TYPE_TO_ENUM_MAP.get(type);
    if (typeEnum == null) {
        return null;
    }
    return Objects.isNull(capacity) ? typeEnum.of() : typeEnum.of(capacity);
}

private static final int DEFAULT_CAPACITY = 1024;

static {
    ServiceLoaderRegistry.register(CustomBlockingQueue.class);
}

private static <T> BlockingQueue<T> customOrDefaultQueue(Integer capacity, Predicate<CustomBlockingQueue> predicate) {
    Collection<CustomBlockingQueue> customBlockingQueues = ServiceLoaderRegistry
            .getSingletonServiceInstances(CustomBlockingQueue.class);

    Integer resolvedCapacity = capacity;
    if (resolvedCapacity == null || resolvedCapacity <= 0) {
        resolvedCapacity = DEFAULT_CAPACITY;
    }

    Integer finalResolvedCapacity = resolvedCapacity;
    return customBlockingQueues.stream()
            .filter(predicate)
            .map(each -> each.generateBlockingQueue(finalResolvedCapacity))
            .findFirst()
            .orElseGet(() -> new LinkedBlockingQueue<T>(finalResolvedCapacity));
}

/**
 * Creates a BlockingQueue with the given {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name}
 * and capacity. if can't find the blockingQueueName with {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name},
 * create custom or default BlockingQueue {@link BlockingQueueTypeEnum#customOrDefaultQueue BlockingQueueTypeEnum.customOrDefaultQueue}.
 *
 * @param blockingQueueName {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name}
 * @param capacity          the capacity of the BlockingQueue
 * @param <T>               the class of the objects in the BlockingQueue
 * @return a BlockingQueue view of the specified T
 */
public static <T> BlockingQueue<T> createBlockingQueue(String blockingQueueName, Integer capacity) {
    final BlockingQueue<T> of = of(blockingQueueName, capacity);
    if (of != null) {
        return of;
    }

    return customOrDefaultQueue(capacity,
            (customerQueue) -> Objects.equals(customerQueue.getName(), blockingQueueName));
}

/**
 * Creates a BlockingQueue with the given {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
 * and capacity. if can't find the blockingQueueName with {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type},
 * create custom or default BlockingQueue {@link BlockingQueueTypeEnum#customOrDefaultQueue BlockingQueueTypeEnum.customOrDefaultQueue}.
 *
 * @param type     {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
 * @param capacity the capacity of the BlockingQueue
 * @param <T>      the class of the objects in the BlockingQueue
 * @return a BlockingQueue view of the specified T
 */
public static <T> BlockingQueue<T> createBlockingQueue(int type, Integer capacity) {
    final BlockingQueue<T> of = of(type, capacity);
    if (of != null) {
        return of;
    }

    return customOrDefaultQueue(capacity,
            (customeQueue) -> Objects.equals(customeQueue.getType(), type));
}

/**
 * Map {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type } to {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name }
 * or "" if can't mapping.
 *
 * @param type {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
 * @return {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name } or "".
 */
public static String getBlockingQueueNameByType(int type) {
    return Optional.ofNullable(TYPE_TO_ENUM_MAP.get(type))
            .map(value -> value.getName())
            .orElse("");
}

/**
 * find {@link BlockingQueueTypeEnum} by {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name }
 * or {@link BlockingQueueTypeEnum#LINKED_BLOCKING_QUEUE} if can't mapping.
 *
 * @param name {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name }
 * @return enum  {@link BlockingQueueTypeEnum}
 */
public static BlockingQueueTypeEnum getBlockingQueueTypeEnumByName(String name) {
    return Optional.ofNullable(NAME_TO_ENUM_MAP.get(name))
            .orElse(LINKED_BLOCKING_QUEUE);
}

三.代理模式

线程池作为任务并发处理的核心组件,其稳定性直接影响系统整体吞吐与响应能力。而线程池中的拒绝策略 ,作为最后一道防线,往往代表了系统已出现短时瓶颈或配置不合理等问题

当出现拒绝策略的时候 我们想要 上报报警 或者说记录指标

但原生的这种拒绝策略不支持我们完成这一点 所以就需要代理模式

这个模式我在第一篇文章做过详细的说明 此处为了节省篇幅 不做过多赘述 juejin.cn/post/759586…

四.观察者模式

大多数动态线程池都会支持其他组件的线程池 比如Hippo4j 它支持了 如此多的组件

image.png

在动态线程池的演进过程中,多组件适配带来的代码耦合问题日益凸显。例如,为了支持 Tomcat 或 RabbitMQ 的线程池动态调整,我们往往需要在核心业务层硬编码大量的特定校验逻辑。

这种做法严重违背了**‘对扩展开放,对修改关闭’(Open/Closed Principle)**的设计原则。我们需要构建一种更优雅的适配机制,使得核心业务层无需感知具体组件的存在,而是通过抽象层面的统一调用,实现对各类异构线程池的无缝兼容。

我们需要一种机制,能够完成以下需求:

  • 解耦模块依赖:配置中心 Starter 无需直接依赖具体的线程池管理模块。
  • 支持动态扩展:新增线程池适配器时,无需修改现有代码。
  • 保持高内聚:每个模块专注自己的核心职责。
  • 简化测试:模块间松耦合,便于单元测试和集成测试。

这时我们就需要引入观察者模式 观察者模式是一对多的模型 一个事件源对应多个观察者

观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新

我们需要定义

  • Subject(主题/被观察者) :维护观察者列表,提供注册、移除观察者的方法。
  • Observer(观察者) :定义更新接口,当收到主题通知时执行相应操作。
  • ConcreteSubject(具体主题) :实现主题接口,状态改变时通知所有观察者。
  • ConcreteObserver(具体观察者) :实现观察者接口,定义具体的更新逻辑。

下面给出简单Demo帮你理解观察者模式

为了让你一秒看懂,我们用**“微信群”**来举例:

  • 被观察者(Subject): 微信群(群主发消息)。
  • 观察者(Observer): 群成员(手机收到弹窗)。

1. 两个核心接口(标准)

Java

// 1. 【接收者标准】观察者接口
// 所有的“群成员”都必须实现这个接口,才能收到消息
interface Observer {
    void update(String message);
}

// 2. 【发送者标准】主题接口
// 定义“群”的基本功能:拉人进群、踢人、群发消息
interface Subject {
    void attach(Observer observer); // 关注/进群
    void detach(Observer observer); // 取消关注/退群
    void notifyObservers(String message); // 通知所有
}

2. 具体实现类

Java

import java.util.ArrayList;
import java.util.List;

// 3. 【具体的发送者】微信群
class WeChatGroup implements Subject {
    // 通讯录:存所有群成员
    private List<Observer> members = new ArrayList<>();

    @Override
    public void attach(Observer observer) {
        members.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        members.remove(observer);
    }

    @Override
    public void notifyObservers(String message) {
        // 核心逻辑:遍历名单,挨个调用 update
        for (Observer member : members) {
            member.update(message);
        }
    }
}

// 4. 【具体的接收者】用户
class User implements Observer {
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + " 收到消息: " + message);
    }
}

3. 测试运行

Java

public class SimpleDemo {
    public static void main(String[] args) {
        // 1. 创建一个群(被观察者)
        WeChatGroup group = new WeChatGroup();

        // 2. 创建三个用户(观察者)
        User userA = new User("张三");
        User userB = new User("李四");
        User userC = new User("王五");

        // 3. 进群(注册)
        group.attach(userA);
        group.attach(userB);
        group.attach(userC);

        // 4. 群主发消息(触发通知)
        System.out.println("====== 第一次群发 ======");
        group.notifyObservers("今晚8点发红包!");

        // 5. 王五退群(移除)
        group.detach(userC);

        // 6. 再次发消息
        System.out.println("\n====== 第二次群发 ======");
        group.notifyObservers("红包发完了!");
    }
}

4. 运行结果

Plaintext

====== 第一次群发 ======
张三 收到消息: 今晚8点发红包!
李四 收到消息: 今晚8点发红包!
王五 收到消息: 今晚8点发红包!

====== 第二次群发 ======
张三 收到消息: 红包发完了!
李四 收到消息: 红包发完了!

总的来说 不管多复杂的框架(Spring Event、Vue 的双向绑定、Hippo4j 的动态刷新),底层逻辑永远只有这三步:

  1. 存起来: 用一个 List 把想要听消息的人存着。
  2. 发生变化: 触发某个事件。
  3. 遍历调方法: 循环 List,调用每个人的 .update() 方法。

下面来讲观察者模式在动态线程池的使用 上面提到了如果我们不做任何设计 在业务层就需要做许多逻辑校验 不利于扩展 所以我们可以利用 Spring 事件机制来实现观察者模式

  1. 事件定义 (ThreadPoolConfigUpdateEvent) :定义了“发生了什么”(配置更新了)以及“携带的数据”(最新的配置属性)。
  2. 事件发布者 (AbstractDynamicThreadPoolRefresher) :作为 Subject(被观察者),负责感知外部配置变化,解析数据,然后广播事件。它不需要知道谁在监听,只管喊一声。
  3. 事件监听者 (Listener) :作为 Observer(观察者),实现了 ApplicationListener。不同的监听器关注不同的业务(一个管普通线程池,一个管 Web 容器线程池),它们同时监听同一个事件,互不干扰。

Java

/* * ========================================================================
 * 1. 【事件定义】(The Message)
 * 继承 Spring 的 ApplicationEvent,作为观察者模式中传递数据的载体。
 * ========================================================================
 */
public class ThreadPoolConfigUpdateEvent extends ApplicationEvent {

    @Getter
    @Setter
    private BootstrapConfigProperties bootstrapConfigProperties;

    /**
     * @param source 事件源(谁发出的事件)
     * @param bootstrapConfigProperties 携带的核心数据(最新的配置)
     */
    public ThreadPoolConfigUpdateEvent(Object source, BootstrapConfigProperties bootstrapConfigProperties) {
        super(source);
        this.bootstrapConfigProperties = bootstrapConfigProperties;
    }
}

/* * ========================================================================
 * 2. 【事件发布者 / 被观察者】(Subject)
 * 负责解析配置,并利用 Spring 上下文广播事件。
 * 这里的核心优势是:它不需要依赖任何具体的 ThreadPoolService。
 * ========================================================================
 */
@Slf4j
@RequiredArgsConstructor
public abstract class AbstractDynamicThreadPoolRefresher implements ApplicationRunner {

    protected final BootstrapConfigProperties properties;

    /**
     * 配置刷新的入口方法
     * 流程:Nacos/Apollo 推送字符串 -> 解析为对象 -> 广播事件
     */
    @SneakyThrows
    public void refreshThreadPoolProperties(String configInfo) {
        // [Step 1] 解析配置:将简单的 String 解析为 Map
        Map<Object, Object> configInfoMap = ConfigParserHandler.getInstance()
            .parseConfig(configInfo, properties.getConfigFileType());
        
        // [Step 2] 数据绑定:利用 Spring Binder 将 Map 绑定到强类型配置对象 properties 中
        ConfigurationPropertySource sources = new MapConfigurationPropertySource(configInfoMap);
        Binder binder = new Binder(sources);
        BootstrapConfigProperties refresherProperties = binder
            .bind(BootstrapConfigProperties.PREFIX, Bindable.ofInstance(properties))
            .get();

        // [Step 3] ★★★ 核心设计模式体现:发布事件 ★★★
        // 这里完全解耦:refresher 不调用 service.update(),而是广播 "配置变了" 的消息。
        // ApplicationContextHolder 是 Spring Context 的工具类
        ApplicationContextHolder.publishEvent(
            new ThreadPoolConfigUpdateEvent(this, refresherProperties)
        );
    }
}

/* * ========================================================================
 * 3. 【具体观察者 A】(Observer - 普通线程池)
 * 监听 ThreadPoolConfigUpdateEvent 事件,负责刷新业务中的动态线程池。
 * ========================================================================
 */
@Slf4j
@RequiredArgsConstructor
public class DynamicThreadPoolRefreshListener implements ApplicationListener<ThreadPoolConfigUpdateEvent> {

    private final NotifierDispatcher notifierDispatcher;

    @Override
    public void onApplicationEvent(ThreadPoolConfigUpdateEvent event) {
        // 1. 从事件中取出最新的配置数据
        BootstrapConfigProperties refresherProperties = event.getBootstrapConfigProperties();

        // 2. 校验:如果没有相关配置,直接跳过
        if (CollUtil.isEmpty(refresherProperties.getExecutors())) {
            return;
        }

        // 3. 执行具体的业务逻辑:遍历并更新线程池参数
        for (ThreadPoolExecutorProperties remoteProperties : refresherProperties.getExecutors()) {
            // ... (省略具体更新代码:比如 setCorePoolSize, setMaximumPoolSize)
            // checkAndRefresh(remoteProperties);
        }
    }
}

/* * ========================================================================
 * 4. 【具体观察者 B】(Observer - Web容器线程池)
 * 同样监听 ThreadPoolConfigUpdateEvent 事件,但只关注 Web 容器(如 Tomcat)的配置。
 * * ★ 设计亮点:通过增加一个新的 Listener 类来扩展功能,完全符合【开闭原则】。
 * 如果未来要支持 RocketMQ 线程池刷新,只需再写一个 RocketMQThreadPoolRefreshListener 即可,
 * 无需修改 Refresher 和其他的 Listener。
 * ========================================================================
 */
@RequiredArgsConstructor
public class WebThreadPoolRefreshListener implements ApplicationListener<ThreadPoolConfigUpdateEvent> {

    private final WebThreadPoolService webThreadPoolService;
    private final NotifierDispatcher notifierDispatcher;

    @Override
    public void onApplicationEvent(ThreadPoolConfigUpdateEvent event) {
        // 1. 获取 Web 相关的配置子集
        BootstrapConfigProperties.WebThreadPoolExecutorConfig webExecutorConfig = event.getBootstrapConfigProperties().getWeb();
        if (Objects.isNull(webExecutorConfig)) {
            return;
        }

        // 2. 获取当前运行时的 Web 线程池指标(用于对比是否发生变化)
        WebThreadPoolBaseMetrics basicMetrics = webThreadPoolService.getBasicMetrics();
        
        // 3. 对比:只有核心参数(核心数、最大数、空闲时间)变了才执行刷新
        if (!Objects.equals(basicMetrics.getCorePoolSize(), webExecutorConfig.getCorePoolSize())
                || !Objects.equals(basicMetrics.getMaximumPoolSize(), webExecutorConfig.getMaximumPoolSize())
                || !Objects.equals(basicMetrics.getKeepAliveTime(), webExecutorConfig.getKeepAliveTime())) {
            
            // [Action] 执行 Tomcat/Jetty 线程池的参数修改
            webThreadPoolService.updateThreadPool(BeanUtil.toBean(webExecutorConfig, WebThreadPoolConfig.class));

            // [Notify] 发送变更通知(钉钉/企业微信)
            sendWebThreadPoolConfigChangeMessage(basicMetrics, webExecutorConfig);
        }
    }
    
    // ... 辅助方法 sendWebThreadPoolConfigChangeMessage ...
}

假设我们需要再添加一个支持RocketMq线程池 只需要创建监听器 实现配置类 自定义事件即可 消除了耦合

结尾

回顾整个动态线程池的开发过程,从最初的一个简单 ThreadPoolExecutor 包装类,到如今支持多组件适配、动态热更新、监控告警的完整中间件,设计模式在其中扮演了“骨架”与“灵魂”的角色。

  • Builder 模式 让我们从冗长的构造参数中解脱,赋予了代码优雅的构建能力;
  • 简单工厂模式 屏蔽了复杂的对象创建逻辑,让策略的扩展变得从容;
  • 代理模式 为我们在不侵入业务代码的前提下,装上了监控的“眼睛”;
  • 观察者模式 则打通了配置中心与应用内部的任督二脉,实现了真正意义上的“动态”与“解耦”。

很多人常说“不要重复造轮子”,但我认为,为了学习而造轮子是程序员进阶的必经之路。当我们不再满足于仅仅“使用”API,而是开始思考“由于什么痛点,所以使用了什么模式,最终解决了什么问题”时,我们的架构思维才真正开始觉醒。

。希望这篇文章能给你带来一些关于源码阅读与架构设计的启发。如果你对代码细节感兴趣,欢迎点击开头的链接查看我的前两篇文章,也欢迎在评论区与我交流!