Java线程池

在 Java 高并发编程中,线程池是绕不开的核心技术,也是面试必问的重点模块。它解决了频繁创建 / 销毁线程带来的性能损耗,实现了线程复用、任务管理、并发控制等核心能力。

一、线程池整体架构:Executor 体系

Java 线程池基于 Executor 框架实现,核心类关系如下:

  • Executor :顶层接口,只有一个 execute() 方法,功能过于简单。
  • ExecutorService:继承 Executor,扩展了 submit、shutdown、invokeAll 等能力,是实际开发中最常用的接口。
  • ThreadPoolExecutor:线程池真正的实现类,所有线程池最终都通过它创建。
  • Executors:工具类,提供快速创建线程池的静态方法。

execute 和 submit 的区别(高频面试题)

  1. 归属不同

    • execute() 是顶层接口 Executor 的方法。
    • submit()ExecutorService 的方法。
  2. 参数与返回值

    • execute() 只支持 Runnable无返回值
    • submit() 支持 Runnable/Callable,返回 Future 对象,可获取结果、取消任务、判断执行状态。
  3. 异常处理

    • execute() 提交的任务异常无法直接捕获,会直接抛出。
    • submit() 可通过 Future.get() 捕获任务内部异常。

二、Executors 工具类:快速创建线程池

JDK 提供 Executors 工具类,可以快速创建几种常用线程池:

1. 普通线程池示例

复制代码
public class ExecutorsDemo {
    public static void main(String[] args) throws Exception {
        // 创建单线程线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        // submit 提交带返回值的任务
        Future<String> future = executorService.submit(() -> {
            System.out.println(Thread.currentThread().getName() + " 任务执行中...");
            return "线程任务执行完成";
        });

        // 获取返回结果
        String result = future.get();
        System.out.println("主线程获取结果:" + result);

        executorService.shutdown();
    }
}

2. 定时任务线程池(延迟执行)

复制代码
public class ScheduledThreadPoolDemo {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
        System.out.println("当前时间:" + new Date());

        // 延迟 5 秒执行
        pool.schedule(() -> {
            System.out.println(Thread.currentThread().getName() + " 延迟任务执行 " + new Date());
        }, 5, TimeUnit.SECONDS);

        pool.shutdown();
    }
}

3. 定时任务:延迟 + 固定周期执行

复制代码
public class ScheduledAtFixRateDemo {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);

        // 初始延迟5秒,之后每隔1秒执行一次
        pool.scheduleAtFixedRate(() -> {
            System.out.println(Thread.currentThread().getName() + " 周期任务执行 " + new Date());
        }, 5, 1, TimeUnit.SECONDS);
    }
}

注意:周期执行任务不能调用 shutdown,否则线程池关闭,任务无法继续。


三、线程池底层核心:7 大参数(必须背会)

ThreadPoolExecutor 的构造方法是线程池的灵魂,包含 7 个核心参数

复制代码
ThreadPoolExecutor(
    int corePoolSize,		// 核心线程数
    int maximumPoolSize,	// 最大线程数
    long keepAliveTime,		// 非核心线程空闲存活时间
    TimeUnit unit,			// 时间单位
    BlockingQueue<Runnable> workQueue, // 任务队列
    ThreadFactory threadFactory,	     // 线程工厂
    RejectedExecutionHandler handler     // 拒绝策略
)

参数说明

  1. corePoolSize常驻核心线程数,即使空闲也不会被销毁(默认)。

  2. maximumPoolSize 线程池能容纳的最大线程总数,必须 ≥1。

  3. keepAliveTime + unit当线程数 > corePoolSize 时,多余空闲线程的存活时间,超时会被回收。

  4. workQueue 任务队列,用于存放提交但未执行的任务。常用:ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue

  5. threadFactory创建线程的工厂,默认即可,也可自定义线程名、优先级。

  6. handler拒绝策略:队列满 + 线程数达到 maximumPoolSize 时触发。


四、线程池工作流程(底层原理)

提交任务后,线程池执行流程如下:

  1. 刚创建线程池时,线程数为 0
  2. 任务提交:
    • 运行线程 < corePoolSize:立即创建核心线程执行任务
    • 运行线程 ≥ corePoolSize:任务进入队列等待
    • 队列满了,但运行线程 < maximumPoolSize:创建非核心线程执行任务
    • 队列满 + 运行线程 = maximumPoolSize:触发拒绝策略
  3. 线程执行完任务后,会从队列复用线程取任务执行
  4. 非核心线程空闲超过 keepAliveTime,会被销毁,最终线程数收缩回 corePoolSize。

一句话总结:先核心线程 → 再队列 → 再非核心线程 → 最后拒绝策略


五、4 种内置拒绝策略

JDK 提供 4 种实现 RejectedExecutionHandler 的拒绝策略:

  1. AbortPolicy(默认) 直接抛出 RejectedExecutionException,阻止系统继续运行。

  2. CallerRunsPolicy 任务回退到调用者线程执行,不抛异常、不丢弃,降低提交速度。

  3. DiscardOldestPolicy 丢弃队列中等待最久的任务,再尝试加入当前任务。

  4. DiscardPolicy 静默丢弃任务,不抛异常、不处理,允许任务丢失时使用。

也可以自定义拒绝策略 ,实现 RejectedExecutionHandler 接口即可。


六、重点:为什么阿里规范禁止用 Executors?

《阿里巴巴 Java 开发手册》明确规定:

线程池不允许使用 Executors 创建,必须通过 ThreadPoolExecutor 自定义。

原因:

  • Executors.newFixedThreadPool / newSingleThreadExecutor队列是 LinkedBlockingQueue,容量为 Integer.MAX_VALUE,会导致大量任务堆积,OOM 内存溢出
  • Executors.newCachedThreadPool最大线程数是 Integer.MAX_VALUE,可能创建大量线程,导致 OOM。

结论:生产环境必须自定义线程池,明确核心线程、最大线程、队列容量、拒绝策略。


七、自定义线程池(企业级标准写法)

复制代码
class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 执行业务");
    }
}

public class CustomThreadPoolDemo {
    public static void main(String[] args) {
        // 自定义线程池
        ExecutorService pool = new ThreadPoolExecutor(
                2,                  // corePoolSize
                5,                  // maximumPoolSize
                2,                  // keepAliveTime
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),  // 有界队列
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()  // 默认拒绝策略
        );

        try {
            // 模拟提交 9 个任务
            for (int i = 0; i < 9; i++) {
                pool.execute(new MyTask());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
    }
}

任务提交流程:

  • 前 2 个:核心线程执行
  • 接下来 3 个:进入队列
  • 再接下来 3 个:创建非核心线程(总线程达到 5)
  • 第 9 个任务:队列满 + 线程满 → 触发拒绝策略,抛出异常

八、线程池的核心优势总结

  1. 线程复用避免频繁创建 / 销毁线程,大幅降低系统开销。

  2. 控制并发度通过队列和最大线程数,防止无限创建线程导致系统崩溃。

  3. 统一管理线程生命周期自动创建、回收、超时处理,减少资源泄漏。

  4. 提高响应速度任务到达时直接使用已有线程,无需等待线程创建。

  5. 规范安全自定义线程池可避免 OOM,符合企业开发规范。

相关推荐
2401_873204652 小时前
基于C++的区块链实现
开发语言·c++·算法
智算菩萨2 小时前
OpenCV几何图形绘制工具全栈开发:从中文路径支持到交互式GUI的完整实战(附源码)
开发语言·图像处理·人工智能·python·opencv·计算机视觉
亚马逊云开发者2 小时前
article
java·开发语言
集智飞行2 小时前
安装rust和cargo
开发语言·后端·rust
云烟成雨TD2 小时前
Spring AI 1.x 系列【11】基于 PromptTemplate 构建一站式 AI 写作助手
java·人工智能·spring
AI-小柒2 小时前
DataEyes聚合平台新API接入实战指南:从0到1打通实时数据链路
大数据·运维·开发语言·人工智能·python·自动化·lua
2301_776508722 小时前
模板代码优化策略
开发语言·c++·算法
m0_726965982 小时前
关于文件上传
开发语言·python