Java 线程池预热(Warm-up)实战

 更新时间:2026年02月11日 09:38:37   作者:Knight_AL  
本文主要介绍了Java 线程池预热(Warm-up)实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、什么是线程池预热?

线程池第一次执行任务时会:

  • 创建核心线程
  • 加载类
  • JIT 编译热点方法
  • 建立资源连接(DB / Redis / RPC)

这些都需要时间,因此第一次执行任务一般比后续慢很多

线程池预热(warm-up)就是:

✔ 在系统启动时提前创建线程
✔ 并让线程执行一次轻量任务
使其进入 已加载、已编译、已就绪 的状态。

二、Demo:不开启预热 vs 开启预热

我们做两个测试:

  1. 未预热 → 首次任务延迟明显更高
  2. 预热后 → 首次任务也能保持正常速度

三、测试代码

以下代码包含两个方法:
testWithoutWarmUp() 未预热
testWithWarmUp() 已预热

完整可运行 Demo:ThreadPoolWarmUpDemo.java

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

public class ThreadPoolWarmUpDemo {

    public static void main(String[] args) throws Exception {
        System.out.println("========== 未开启预热,测试开始 ==========");
        testWithoutWarmUp();

        System.out.println("\n========== 开启预热后,测试开始 ==========");
        testWithWarmUp();
    }

    /** 用于模拟“第一次慢,后面快” */
    private static final AtomicBoolean firstRun = new AtomicBoolean(true);

    /** 模拟核心业务逻辑 */
    private static void mockBizWork() {
        try {
            if (firstRun.compareAndSet(true, false)) {
                // 只在第一次执行时慢
                Thread.sleep(50);
            } else {
                // 后续任务很快
                Thread.sleep(2);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    /** 创建一个全新的线程池(关键) */
    private static ThreadPoolExecutor newExecutor() {
        return new ThreadPoolExecutor(
                4, 4,
                60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>()
        );
    }

    /** 未开启线程池预热 */
    public static void testWithoutWarmUp() throws Exception {
        ThreadPoolExecutor executor = newExecutor();
        firstRun.set(true);

        long start = System.currentTimeMillis();

        Future<Long> future = executor.submit(() -> {
            long s = System.currentTimeMillis();
            mockBizWork();
            return System.currentTimeMillis() - s;
        });

        long firstCost = future.get();
        long totalCost = System.currentTimeMillis() - start;

        System.out.println("第一次任务耗时 = " + firstCost + " ms");
        System.out.println("整体耗时(含线程创建)= " + totalCost + " ms");

        executor.shutdown();
    }

    /** 开启预热 */
    public static void testWithWarmUp() throws Exception {
        ThreadPoolExecutor executor = newExecutor();
        firstRun.set(true);

        System.out.println(">>> 开始预热核心线程...");
        executor.prestartAllCoreThreads();

        // 预热任务:吃掉“第一次慢”
        for (int i = 0; i < 4; i++) {
            executor.submit(ThreadPoolWarmUpDemo::mockBizWork).get();
        }

        System.out.println(">>> 预热完成!");

        long start = System.currentTimeMillis();

        Future<Long> future = executor.submit(() -> {
            long s = System.currentTimeMillis();
            mockBizWork();
            return System.currentTimeMillis() - s;
        });

        long cost = future.get();
        long totalCost = System.currentTimeMillis() - start;

        System.out.println("第一次任务耗时(已预热)= " + cost + " ms");
        System.out.println("整体耗时(线程已就绪)= " + totalCost + " ms");

        executor.shutdown();
    }
}

四、对比图(示例)

场景首次任务耗时备注
❄️ 未预热55 ms包含线程创建、类加载等
🔥 已预热3 ms线程已创建、JIT 已编译,几乎“秒回”

五、什么时候必须使用线程池预热?

以下场景建议开启:

✔ 高并发系统(交易、支付、秒杀)

首个请求慢会直接影响用户体验和系统稳定性。

✔ 微服务自动扩容(K8S / Spring Cloud)

Pod 刚拉起来就被流量打满,如果没预热会导致:

  • 错误率突然增加
  • P99 延迟飙升
  • 下游熔断

✔ 接口对响应时间敏感(推荐、风控、广告)

冷启动会影响模型推理链路整体延迟。

六、总结

线程池预热很简单,但效果极其明显。

👉 不预热 = 首次任务慢几十毫秒
👉 预热后 = 首次任务几毫秒完成

通过:

executor.prestartAllCoreThreads();
executor.submit(warmTask);

即可大幅减少冷启动延迟,保证服务稳定性和低延迟能力。

到此这篇关于Java 线程池预热(Warm-up)实战的文章就介绍到这了,更多相关Java 线程池预热内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JAVA抛出异常的三种形式详解

    JAVA抛出异常的三种形式详解

    这篇文章主要介绍了JAVA抛出异常的三种形式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • java Collections 排序--多条件排序实例

    java Collections 排序--多条件排序实例

    这篇文章主要介绍了java Collections 排序--多条件排序实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • Java BigInteger类,BigDecimal类,Date类,DateFormat类及Calendar类用法示例

    Java BigInteger类,BigDecimal类,Date类,DateFormat类及Calendar类用法示例

    这篇文章主要介绍了Java BigInteger类,BigDecimal类,Date类,DateFormat类及Calendar类用法,结合实例形式详细分析了Java使用BigInteger类,BigDecimal类,Date类,DateFormat类及Calendar类进行数值运算与日期运算相关操作技巧,需要的朋友可以参考下
    2019-03-03
  • mybatis插件实现自定义改写表名实例代码

    mybatis插件实现自定义改写表名实例代码

    在数据库操作过程中,经常有修改表名的需求,下面这篇文章主要给大家介绍了关于mybatis插件实现自定义改写表名的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-04-04
  • Springboot3集成Knife4j的步骤以及使用(最完整版)

    Springboot3集成Knife4j的步骤以及使用(最完整版)

    这篇文章主要介绍了Springboot3集成Knife4j的步骤以及使用的相关资料,Knife4j是一种增强Swagger的工具,支持黑色主题和更多配置选项,它与swagger-bootstrap-ui相比,提供了更现代的外观和更多的功能,需要的朋友可以参考下
    2024-11-11
  • Java关于重排链表详细解析

    Java关于重排链表详细解析

    在我们平时的代码过程中,链表是我们经常遇到的一个数据结构,面试题中链表占很大一部分,可见链表操作是非常重要的。本篇文章我们来探究一下如何进行重排链表
    2022-01-01
  • Java Mybatis使用resultMap时,属性赋值顺序错误的巨坑

    Java Mybatis使用resultMap时,属性赋值顺序错误的巨坑

    这篇文章主要介绍了Java Mybatis使用resultMap时,属性赋值顺序错误的巨坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • MyBatis一对一级联更新问题小结

    MyBatis一对一级联更新问题小结

    日常工作中经常会涉及到一对一级联更新的问题,本文主要介绍了MyBatis一对一级联更新问题小结,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • 详解微信开发之Author网页授权

    详解微信开发之Author网页授权

    微信开发中,经常有这样的需求:获得用户头像、绑定微信号给用户发信息,那么实现这些的前提就是授权!本文对此进行系统介绍,需要的朋友一起来看下吧
    2016-12-12
  • Spring Validation数据校验详解

    Spring Validation数据校验详解

    SpringValidation是Spring框架的数据校验抽象层,支持注解、分组、嵌套校验及国际化,默认集成HibernateValidator,可自定义校验器满足复杂需求,适用于Web参数校验和业务逻辑验证
    2025-09-09

最新评论