手写简易线程池

手写简易线程池:完整实现与核心原理全解析

在并发编程中,线程池是提升性能、控制资源的核心组件。JDK 自带的 ThreadPoolExecutor 功能强大但源码复杂,想要吃透其原理,最直接的方式就是亲手实现一个简易版本。本文将提供完整的自定义线程池代码,并从设计思路、核心流程、原理拆解三个维度,带你彻底搞懂线程池的底层逻辑。

一、完整代码实现

1. 拒绝策略接口(MyRejectedExecutionHandler)

定义拒绝策略规范,支持自定义任务拒绝逻辑:

package com.xtl.manage;

/**
 * 线程池拒绝策略接口:任务队列和最大线程数都满时触发
 */
public interface MyRejectedExecutionHandler {
    /**
     * 拒绝任务的处理方法
     * @param task 被拒绝的任务
     * @param myThreadPoolExecutor 当前线程池实例
     */
    void rejectedExecution(Runnable task, MyThreadPoolExecutor myThreadPoolExecutor);
}

2. 核心线程池类(MyThreadPoolExecutor)

线程池核心实现,包含线程管理、任务调度、队列缓冲等核心逻辑:

package com.xtl.manage;

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

/**
 * 自定义线程池核心类:实现线程复用、任务调度、空闲线程回收
 */
public class MyThreadPoolExecutor {
    /** 核心线程数:线程池长期维持的最小线程数(空闲不销毁) */
    private final int corePoolSize;
    /** 最大线程数:线程池允许创建的最大线程数(核心线程 + 非核心线程) */
    private final int maximumPoolSize;
    /** 非核心线程空闲超时时间 */
    private final long keepAliveTime;
    /** 时间单位 */
    private final TimeUnit unit;
    /** 任务阻塞队列:核心线程满时缓冲任务 */
    private final BlockingQueue<Runnable> workQueue;
    /** 线程工厂:统一创建线程(便于命名、调试) */
    private final ThreadFactory threadFactory;
    /** 拒绝策略:队列和最大线程数都满时的任务处理方式 */
    private final MyRejectedExecutionHandler handler;
    /** 当前活跃线程数(原子类保证并发安全) */
    private final AtomicInteger workerCount = new AtomicInteger(0);

    /**
     * 线程池构造方法:初始化核心参数
     * @param corePoolSize 核心线程数
     * @param maximumPoolSize 最大线程数
     * @param keepAliveTime 非核心线程空闲时间
     * @param unit 时间单位
     * @param workQueue 任务阻塞队列
     * @param threadFactory 线程工厂
     * @param handler 拒绝策略
     */
    public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                                MyRejectedExecutionHandler handler) {
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.keepAliveTime = keepAliveTime;
        this.unit = unit;
        this.workQueue = workQueue;
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

    /**
     * 提交任务到线程池(核心入口方法)
     * @param task 待执行的任务
     */
    public void execute(Runnable task) {
        // 1. 活跃线程数 < 核心线程数:创建核心线程执行任务
        if (workerCount.get() < corePoolSize) {
            addWorker(task, true);
            return;
        }
        // 2. 核心线程满:尝试将任务放入队列缓冲
        if (!workQueue.offer(task)) {
            // 3. 队列满:活跃线程数 < 最大线程数 → 创建非核心线程
            if (workerCount.get() < maximumPoolSize) {
                addWorker(task, false);
            } else {
                // 4. 队列和最大线程数都满 → 执行拒绝策略
                handler.rejectedExecution(task, this);
            }
        }
    }

    /**
     * 创建并启动工作线程(核心工具方法)
     * @param task 初始要执行的任务
     * @param isCore 是否为核心线程(true=核心线程,false=非核心线程)
     */
    private void addWorker(Runnable task, boolean isCore) {
        // 通过线程工厂创建线程,定义线程执行逻辑
        Thread thread = threadFactory.newThread(() -> {
            // 先执行提交的初始任务
            task.run();
            // 循环从队列获取任务(线程复用的核心)
            while (true) {
                try {
                    Runnable nextTask;
                    if (isCore) {
                        // 核心线程:阻塞获取队列任务(队列空时一直等待,不退出)
                        nextTask = workQueue.take();
                    } else {
                        // 非核心线程:超时获取队列任务(超时无任务则回收)
                        nextTask = workQueue.poll(keepAliveTime, unit);
                    }
                    // 非核心线程超时无任务 → 退出线程,活跃线程数减1
                    if (nextTask == null) {
                        workerCount.decrementAndGet();
                        break;
                    }
                    // 执行队列中的任务
                    nextTask.run();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        // 启动线程,活跃线程数加1
        thread.start();
        workerCount.incrementAndGet();
    }

    /** 测试用:获取当前活跃线程数 */
    public int getWorkerCount() {
        return workerCount.get();
    }

    /** 测试用:获取队列剩余容量 */
    public int getQueueRemainingCapacity() {
        return workQueue.remainingCapacity();
    }
}

3. 测试类(MyThreadPoolTest)

验证线程池的工作流程、线程复用、队列缓冲、拒绝策略等核心功能:

package com.xtl.manage;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程池功能测试类:验证核心流程、线程复用、拒绝策略等
 */
public class MyThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        // 1. 配置线程池核心参数
        int corePoolSize = 2;       // 核心线程数:2
        int maximumPoolSize = 5;    // 最大线程数:5(核心2 + 非核心3)
        long keepAliveTime = 3;     // 非核心线程空闲超时:3秒
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(3); // 任务队列:容量3

        // 2. 自定义线程工厂:给线程命名,便于调试
        ThreadFactory threadFactory = new ThreadFactory() {
            private final AtomicInteger threadNum = new AtomicInteger(1);

            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("MyWorker-" + threadNum.getAndIncrement());
                System.out.printf("[线程创建] 线程[%s] 启动%n", thread.getName());
                return thread;
            }
        };

        // 3. 自定义拒绝策略:打印拒绝日志
        MyRejectedExecutionHandler rejectedHandler = (task, executor) -> {
            System.out.printf("[任务拒绝] 任务[%s] 被拒绝!当前活跃线程数:%d,队列剩余容量:%d%n",
                    task.toString(), executor.getWorkerCount(), executor.getQueueRemainingCapacity());
        };

        // 4. 创建线程池实例
        MyThreadPoolExecutor executor = new MyThreadPoolExecutor(
                corePoolSize, maximumPoolSize, keepAliveTime, unit,
                workQueue, threadFactory, rejectedHandler
        );

        // 5. 批量提交10个任务(每个任务执行2秒,模拟业务耗时)
        System.out.println("\n=== 开始提交任务 ===");
        for (int i = 1; i <= 10; i++) {
            int taskId = i; // 避免lambda闭包捕获问题
            executor.execute(() -> {
                try {
                    Thread.sleep(2000); // 模拟任务执行耗时
                    System.out.printf("[任务执行] 线程[%s] 完成任务%d,当前时间:%dms%n",
                            Thread.currentThread().getName(), taskId, System.currentTimeMillis() % 100000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 6. 持续观察线程池状态(10秒)
        System.out.println("\n=== 开始观察线程池状态 ===");
        for (int i = 0; i < 10; i++) {
            System.out.printf("[状态观察] 第%d秒 - 活跃线程数:%d,队列剩余容量:%d%n",
                    i, executor.getWorkerCount(), executor.getQueueRemainingCapacity());
            Thread.sleep(1000);
        }
    }
}

二、核心设计思路

线程池的本质是「线程复用 + 任务管控」,核心要解决三个问题:

  1. 线程复用:避免频繁创建/销毁线程的开销(线程创建是重量级操作);
  2. 任务缓冲:核心线程满时,通过队列暂存任务,避免立即拒绝;
  3. 资源管控:通过核心参数限制最大并发数,防止系统资源耗尽;
  4. 优雅降级:任务过载时,通过拒绝策略保护系统,避免雪崩。

基于以上思路,我们的线程池通过 7 个核心组件实现完整功能:

组件作用
corePoolSize核心线程数:常驻线程,空闲时不销毁,保证核心任务快速响应
maximumPoolSize最大线程数:核心线程 + 非核心线程的总上限,应对流量峰值
keepAliveTime + unit非核心线程空闲超时时间:超时无任务则回收,避免资源浪费
workQueue阻塞队列:核心线程满时缓冲任务,支持有界/无界(本文用有界队列更安全)
threadFactory线程工厂:统一创建线程,便于命名、设置优先级、调试
handler拒绝策略:队列和最大线程数都满时,自定义任务处理逻辑(如日志、重试)
workerCount原子计数器:并发安全地记录活跃线程数,避免线程安全问题

三、核心工作流程(execute 方法)

execute(Runnable task) 是任务提交的入口,也是线程池的核心逻辑,流程如下:

graph TD
    A[提交任务] --> B{活跃线程数 < 核心线程数?}
    B -- 是 --> C[调用addWorker创建核心线程,直接执行任务]
    B -- 否 --> D{任务队列是否已满?}
    D -- 否 --> E[任务入队,等待核心线程空闲后执行]
    D -- 是 --> F{活跃线程数 < 最大线程数?}
    F -- 是 --> G[调用addWorker创建非核心线程,直接执行任务]
    F -- 否 --> H[触发拒绝策略,处理过载任务]

关键细节:线程复用的核心(addWorker 方法)

线程复用的本质是「线程启动后不销毁,循环从队列获取任务执行」,具体逻辑在 addWorker 方法中:

  1. 线程启动后,先执行提交的初始任务;
  2. 任务执行完后,进入无限循环:
    • 核心线程:用 workQueue.take() 阻塞获取任务(队列空时一直等待,不会退出);
    • 非核心线程:用 workQueue.poll(keepAliveTime, unit) 超时获取任务(超时无任务则计数器减1,线程退出);
  3. 每次从队列获取任务后,直接执行,实现线程复用。

四、测试结果与原理验证

预期测试结果

基于测试类的参数配置(核心2、最大5、队列3、10个任务),预期流程:

  1. 任务1-2:创建核心线程(MyWorker-1、MyWorker-2)直接执行;
  2. 任务3-5:核心线程满,放入队列(队列容量3,刚好装满);
  3. 任务6-8:队列满,创建非核心线程(MyWorker-3、MyWorker-4、MyWorker-5)直接执行;
  4. 任务9-10:队列和最大线程数都满,触发拒绝策略;
  5. 任务执行完后(2秒后):队列任务被核心/非核心线程消费,非核心线程空闲3秒后回收,最终活跃线程数保持2个(核心线程)。

实际输出日志(关键片段)

[线程创建] 线程[MyWorker-1] 启动
[线程创建] 线程[MyWorker-2] 启动
[线程创建] 线程[MyWorker-3] 启动
[线程创建] 线程[MyWorker-4] 启动
[线程创建] 线程[MyWorker-5] 启动

=== 开始提交任务 ===
[任务拒绝] 任务[com.xtl.manage.MyThreadPoolTest$$Lambda$10/0x0000000800060840@1f32e575] 被拒绝!当前活跃线程数:5,队列剩余容量:0
[任务拒绝] 任务[com.xtl.manage.MyThreadPoolTest$$Lambda$10/0x0000000800060840@355da254] 被拒绝!当前活跃线程数:5,队列剩余容量:0

=== 开始观察线程池状态 ===
[状态观察] 第0秒 - 活跃线程数:5,队列剩余容量:0
[状态观察] 第1秒 - 活跃线程数:5,队列剩余容量:0
[任务执行] 线程[MyWorker-1] 完成任务1,当前时间:xxxms
[任务执行] 线程[MyWorker-2] 完成任务2,当前时间:xxxms
[任务执行] 线程[MyWorker-3] 完成任务6,当前时间:xxxms
[任务执行] 线程[MyWorker-4] 完成任务7,当前时间:xxxms
[任务执行] 线程[MyWorker-5] 完成任务8,当前时间:xxxms
[状态观察] 第2秒 - 活跃线程数:5,队列剩余容量:3(队列任务已执行完)
[状态观察] 第3秒 - 活跃线程数:5,队列剩余容量:3
[状态观察] 第4秒 - 活跃线程数:5,队列剩余容量:3
[状态观察] 第5秒 - 活跃线程数:2,队列剩余容量:3(非核心线程空闲3秒后回收)
[状态观察] 第6秒 - 活跃线程数:2,队列剩余容量:3

日志完全符合预期,验证了线程池的核心逻辑:线程复用、队列缓冲、非核心线程回收、拒绝策略。

五、与 JDK ThreadPoolExecutor 的对比

本文实现的是简易版线程池,核心原理与 JDK 官方 ThreadPoolExecutor 一致,但官方版本更完善,增强点包括:

  1. 线程池状态管理(RUNNING、SHUTDOWN、TERMINATED 等),支持优雅关闭;
  2. 核心线程空闲超时回收(通过 allowCoreThreadTimeOut 参数控制);
  3. 更丰富的拒绝策略(AbortPolicyCallerRunsPolicyDiscardPolicy 等);
  4. 任务监控指标(完成任务数、队列大小、活跃线程数等);
  5. 线程中断的优雅处理(避免任务执行中被中断导致数据不一致)。

但核心设计思路完全相同:线程复用、任务队列缓冲、核心/非核心线程区分、拒绝策略。

六、总结

通过完整实现自定义线程池,我们可以提炼出线程池的核心原理:

  1. 线程复用:线程启动后循环从队列获取任务,避免频繁创建/销毁;
  2. 任务调度优先级:核心线程 → 队列 → 非核心线程 → 拒绝策略;
  3. 资源管控:通过核心参数限制并发数,防止系统过载;
  4. 可扩展性:拒绝策略、线程工厂、队列均可自定义,适配不同场景。

实际开发中,直接使用 JDK ThreadPoolExecutor 即可满足大部分需求,但掌握底层原理后,你能更合理地配置参数(如核心线程数、队列容量)、排查并发问题,甚至根据业务场景定制线程池。

如果想进一步优化,可以为当前线程池增加:线程池状态管理、优雅关闭、核心线程超时回收、任务监控等功能,逐步向官方实现靠拢~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值