Java 线程池入门详解:线程池基础定义 + ThreadPoolExecutor 全参构造方法逐参数解析

📚 目录

[1. 线程池是什么](#1. 线程池是什么)

[2. ThreadPoolExecutor 全参构造方法介绍](#2. ThreadPoolExecutor 全参构造方法介绍)

[3. 七大构造参数逐个详解](#3. 七大构造参数逐个详解)

[4. 完整代码演示](#4. 完整代码演示)

[5. 小结](#5. 小结)

前言:

  Java多线程开发里,手动new Thread创建线程弊端极多:频繁创建销毁线程开销大、线程无管控容易无限创建打满CPU、任务无法统一管理。线程池就是用来复用线程、管控任务的核心工具,也是面试高频考点。

1. 线程池是什么

 我们提前创建一批线程,放入到一个池子当中,当我们想要用其中一个线程时,我们可以直接从这个池子中取,用完这个线程时,又把线程放回到线程池中,不会销毁线程;

 这个就好比我们生活中的锅碗瓢盆,当我们想要使用其中一个东西的时候,我们可以直接在厨房中进行取,此时厨房就相当于一个线程池,我们做饭的东西都放在厨房中,锅碗瓢盆等待我们使用;

 总结:线程池是预先创建一批线程存放于池中,程序有异步任务时,直接复用池内空闲线程执行任务;任务执行完毕,线程不会销毁,回归池子等待复用。

 好处:

  1. 降低资源消耗->可以直接对比于一次性碗和陶瓷碗
  2. 提升相应速度->不用重复进行创建,好比做饭直接在厨房中进行拿去即可,不需要重新去超市买
  3. 方便管理 ->集中,并且日常厨房中不需要过多的锅碗瓢盆,我们可以主动限制买多少个
  4. 便于维护和运行->可以统计锅碗瓢盆的使用次数,和使用

[🔙 返回目录](#🔙 返回目录)


2. ThreadPoolExecutor 全参构造方法介绍

 Java中我们可以使用ThreadPoolExecutor构造器创建线程池,也可以使用Executors来创建线程池;

 具体使用哪一种方法,取决于公司的业务要求,ExecutorsJava官方对ThreadPoolExecutor进行进一步封装,方便我们日常中使用;

 而在阿里公司中明确规定书写线程池必须使用ThreadPoolExecutor,防止出现线程安全问题;

java 复制代码
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    //底层源码省略
}

[🔙 返回目录](#🔙 返回目录)


3. 七大构造参数逐个详解

corePoolSize 与 maximumPoolSize

corePoolSize :核心线程数,线程池中不会在空闲时自动回收,是线程池中时常保留的线程;

maximumPoolSize : 最大线程数,线程池中允许创建最大的线程数量,最大线程数 = 核心线程数+临时线程数

 举一个简单的例子:

 一家公司里面有5个正式员工,一天公司业务忙不过来了,老板有两个方案,一个是去招正式员工,一个去招实习生,如果

 招正式员工,等业务空闲的时候,员工就没事干,有的就开始摸鱼了,开除的话又需要考虑赔钱,此时老板就能去招实习

 业务忙的时候就多招实习生,业务不忙了就可以给实习生开了,不需要赔钱,此时一举两得;

 此时:正式员工就相当于核心线程,实习生就相当于临时的
[🔙 返回目录](#🔙 返回目录)


long keepAliveTime 与TimeUnit unit

 两个参数进行搭配使用,管控非核心临时线程的空闲回收规则;

keepAliveTime : 代表非核心线程等待任务的时间,当临时线程无事可做,超出等待实现时,JVA会销毁释放资源;

 默认只对非核心线程生效,如果调用调用threadPool.allowCoreThreadTimeOut(true) 开启后,核心线程同样会遵守该超时规则空闲回收。

unit : 时间单位枚举类TimeUnit,用来指定 keepAliveTime 的时间单位,常使用枚举:

  1. TimeUnit.SECONDS:秒(日常最常用)
  2. TimeUnit.MILLISECONDS:毫秒
  3. TimeUnit.MINUTES:分钟
  4. TimeUnit.HOURS:小时

 具体采用哪一个时间单位,根据需求进行选取;
[🔙 返回目录](#🔙 返回目录)


BlockingQueue<Runnable> workQueue

BlockingQueue<Runnable> workQueue : 阻塞队列,用于存放等待执行的runnable任务,当核心线程都处于忙碌状态时,新提交的任务不会创建新的线程,而是进入阻塞队列进行等待,当队列满之后,线程池才会创建非核心线程;

 主流使用的阻塞队列:

队列类型 特点 生产环境注意事项
ArrayBlockingQueue 有界队列,初始化必须指定固定容量 推荐日常业务使用,容量可控,不会无限堆积任务
LinkedBlockingQueue 可设置容量;不传参则为无界队列 无界模式任务可无限存入,极易引发 OOM 内存溢出,禁止裸用
SynchronousQueue 不存储任何任务,入队操作必须有线程同时接收 没有排队能力,任务进来直接触发扩容线程,线程容易拉满
  1. 队列是线程池流程的第二优先级:核心线程 > 队列 > 临时线程 > 拒绝策略;
  2. 高并发业务优先选用ArrayBlockingQueue限制队列长度,杜绝内存风险;
  3. Executors工具类很多默认使用无界LinkedBlockingQueue,这是阿里规范禁止直接使用 Executors 的核心原因。

[🔙 返回目录](#🔙 返回目录)


ThreadFactory threadFactory

 又名:线程工厂->专门用于自定义创建线程,线程池中所有新线程都由线程工厂产出;

 默认使用Executors.defaultThreadFactory(),创建普通非守护线程、无自定义线程名。

 线程工厂在生产环境中存在的价值:

  1. 自定义线程名称,线上排查日志、堆栈时,根据线程名快速区分业务线程,定位故障

  2. 可以设置守护线程,线程优先级

  3. 可以给线程绑定线程组,方便管理
    [🔙 返回目录](#🔙 返回目录)


RejectedExecutionHandler handler
 全称:拒绝策略,
 触发时机: 核心线程满、阻塞队列塞满、最大线程数也打满,线程池没有任何能力处理新任务时 ,执行拒绝策略。

拒绝策略类 行为逻辑 使用场景
AbortPolicy(默认) 直接抛出RejectedExecutionException异常,中断任务提交 普通业务,希望任务失败立刻报错,方便感知异常
CallerRunsPolicy 让提交任务的调用线程执行当前被拒绝任务 不允许丢任务,削峰限流,最慢但保证任务必执行
DiscardPolicy 默默丢弃新提交的任务,无报错无日志 允许丢任务,非核心日志、埋点等不重要任务
DiscardOldestPolicy 丢掉队列最老未执行任务,尝试放入新任务 时效性优先,新任务价值更高(消息实时推送)

 举一个简单的例子->门店客服:正式员工(核心线程)全忙、等候坐席(队列)坐满、临时兼职(最大线程)也全部在岗。

 再来新客户(新任务),拒绝策略决定怎么处理:

 Abort:直接拒绝接待,告知客户爆满报错;

 CallerRuns:前台接待员自己停下手里工作接待客户;

 Discard:假装没看见,忽略客户;

 DiscardOldest:把排队最久的客户赶走,接待新来的。

生产环境建议:

  1. 绝大多数业务推荐 CallerRunsPolicy,不会丢数据,天然限流;
  2. 定时、埋点、日志上报选 DiscardPolicy;
  3. 时效性强的推送业务用 DiscardOldestPolicy;
  4. 默认 AbortPolicy 容易线上大量抛异常,线上不推荐直接使用。

[🔙 返回目录](#🔙 返回目录)


4. 完整代码演示

java 复制代码
public class Demo1 {

    public static void main(String[] args) {
        //定义线程工厂
        ThreadFactory businessThreadFactory  = new ThreadFactory () {
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("thread-" + threadNumber.incrementAndGet());
                return thread;
            }
        };
        //定义拒绝策略
        RejectedExecutionHandler customHandler = new RejectedExecutionHandler() {

            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("任务被线程池拒绝,可自定义处理逻辑");
            }
        };
        //创建7个构造方法的线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                3,                      // 核心线程数
                8,                                 // 最大线程数
                5L,                                // 非核心线程空闲存活时长
                TimeUnit.SECONDS,                  // 时间单位:秒
                new ArrayBlockingQueue<>(20),     // 有界阻塞队列,容量20
                businessThreadFactory,             // 自定义线程工厂
                customHandler                      // 自定义拒绝策略
        );

        // 批量提交30个测试任务
        for (int i = 1; i <= 30; i++) {
            int taskId = i;
            threadPool.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务:" + taskId);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 程序结束关闭线程池
        threadPool.shutdown();


    }
}

 结果参考:


[🔙 返回目录](#🔙 返回目录)


5. 小结

 构造参数:

参数 作用
corePoolSize 常驻核心线程,不会被超时回收
maximumPoolSize 线程总数上限 = 核心线程 + 临时非核心线程
keepAliveTime 非核心线程空闲多久没人用就销毁
unit 空闲时间单位(时分秒毫秒)
workQueue 存放等待任务的阻塞队列,优先缓冲削峰
threadFactory 负责创建线程,推荐自定义命名方便排错
handler 线程池满载后的兜底拒绝方案

 执行流程:

提交任务 -> 核心线程数量没满:核心线程执行 -> 核心线程满:任务放入阻塞队列 -> 队列存满,创建线程数达到最大线程数 -> 最大线程满,执行拒绝策略

 线程池总结:

线程池通过复用线程减少频繁创建销毁线程的开销,流程是先走核心线程、再走队列、再扩容临时线程,最后触发拒绝;生产环境必须手动指定七大参数,约束队列长度、自定义线程名,根据业务选型拒绝策略,规避 OOM 和线上故障。
[🔙 返回目录](#🔙 返回目录)


相关推荐
tryxr16 分钟前
Chat2Excel 文件服务模块上传文件功能开发
java·项目·oss·easyexcel·文件服务
Tangyuewei19 分钟前
Java 写 Agent:模型只是组件
java·开发语言
小玮看世界1 小时前
[Python]从合并区间到传感器融合区:合并区间在传感器区域融合的实际落地
开发语言·python
彧azz1 小时前
Java学习语法篇:变量
java·学习
繁星蓝雨2 小时前
C++的设计与演进大纲(如何从C语言一步步成长为C++)
c语言·开发语言·c++·c++历史·c++演进
Evand J2 小时前
【MATLAB例程,图像降噪滤波9】自适应维纳滑动窗口滤波(Wiener)图像降噪、方法对比,有中文注释
开发语言·图像处理·计算机视觉·matlab·滑动窗口·滤波·降噪
杜大哥2 小时前
python程序:如何查看电脑【电池电量的剩余百分比】 和 【是否插入连接着充电器】?
开发语言·python
wuyk5552 小时前
Python网络爬虫入门到实战 第01章:爬虫到底是什么?原理、流程、合法性、风险全解析(零基础必看)
开发语言·爬虫·python
信誓旦旦的程序猿2 小时前
【量化系统从零构建 #04】存储设计:选型·建库·交易日历
java·人工智能·python·股票数据api·股票数据·股票数据api接口·股票api数据接口
yurenpai(27届找实习中)2 小时前
Java 10 的 var 为什么不能随便用?从订单汇总看懂类型推断与泛型陷阱
java·开发语言·java18