当前位置: 首页 > news >正文

面试基础--线程生命周期、线程池(ThreadPoolExecutor 工作原理)

深入解析线程生命周期与线程池(ThreadPoolExecutor)的工作原理:从源码到底层实现

在现代高并发的互联网应用中,多线程编程是提升系统性能的重要手段之一。然而,线程的创建、销毁以及管理成本较高,直接使用线程可能会导致系统资源耗尽。为了解决这一问题,Java 提供了线程池(ThreadPoolExecutor)机制,能够高效地管理线程的生命周期,提升系统性能。本文将深入探讨线程的生命周期、线程池的工作原理,并结合底层源码分析其实现细节。


一、线程的生命周期

在 Java 中,线程的生命周期可以分为以下几个状态:

  1. NEW(新建状态)
    线程被创建但尚未启动,此时线程对象已经初始化,但还未调用 start() 方法。

  2. RUNNABLE(可运行状态)
    线程调用了 start() 方法后进入 RUNNABLE 状态。此时线程可能正在运行,也可能在等待 CPU 时间片。

  3. BLOCKED(阻塞状态)
    线程因为等待锁(如 synchronized 关键字)而进入阻塞状态,直到获取到锁后才能回到 RUNNABLE 状态。

  4. WAITING(等待状态)
    线程调用了 wait()join()LockSupport.park() 等方法,进入无限期等待状态,直到其他线程显式唤醒。

  5. TIMED_WAITING(超时等待状态)
    线程调用了 sleep()wait(timeout)join(timeout) 等方法,进入有限期的等待状态。

  6. TERMINATED(终止状态)
    线程执行完毕或因异常退出,进入终止状态。

线程的状态转换可以通过以下代码验证:

Thread thread = new Thread(() -> {
    try {
        Thread.sleep(1000); // TIMED_WAITING
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
System.out.println(thread.getState()); // NEW
thread.start();
System.out.println(thread.getState()); // RUNNABLE
Thread.sleep(500);
System.out.println(thread.getState()); // TIMED_WAITING
thread.join();
System.out.println(thread.getState()); // TERMINATED

二、线程池(ThreadPoolExecutor)的工作原理

线程池的核心思想是复用线程,避免频繁创建和销毁线程带来的性能开销。Java 中的 ThreadPoolExecutor 是线程池的核心实现类,其工作原理可以分为以下几个部分:

1. 核心参数

ThreadPoolExecutor 的构造函数包含以下核心参数:

  • corePoolSize:核心线程数,即使线程空闲也不会被回收。
  • maximumPoolSize:最大线程数,当任务队列满时,线程池会创建新线程,直到达到最大线程数。
  • keepAliveTime:非核心线程的空闲存活时间。
  • workQueue:任务队列,用于存放待执行的任务。
  • threadFactory:线程工厂,用于创建线程。
  • handler:拒绝策略,当任务队列和线程池都满时,如何处理新任务。

2. 任务提交与执行流程

当一个任务提交到线程池时,ThreadPoolExecutor 的执行流程如下:

  1. 如果当前线程数小于 corePoolSize,则创建新线程执行任务。
  2. 如果线程数已达到 corePoolSize,则将任务放入任务队列(workQueue)。
  3. 如果任务队列已满且线程数小于 maximumPoolSize,则创建新线程执行任务。
  4. 如果线程数已达到 maximumPoolSize 且任务队列已满,则根据拒绝策略处理任务。

3. 源码分析

以下是 ThreadPoolExecutor 的核心方法 execute() 的源码解析:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    // 1. 如果当前线程数小于 corePoolSize,则创建新线程
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 2. 尝试将任务加入任务队列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (!isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 3. 如果任务队列已满,尝试创建新线程
    else if (!addWorker(command, false))
        // 4. 如果线程数已达到 maximumPoolSize,执行拒绝策略
        reject(command);
}
  • ctl:是一个原子整数,用于存储线程池的状态和线程数。
  • addWorker:用于创建新线程并执行任务。
  • workQueue.offer:尝试将任务加入任务队列。
  • reject:执行拒绝策略。

4. 线程池的状态

ThreadPoolExecutor 使用 ctl 的高 3 位表示线程池的状态:

  • RUNNING:正常运行状态,可以接收新任务并处理队列中的任务。
  • SHUTDOWN:关闭状态,不再接收新任务,但会处理队列中的任务。
  • STOP:停止状态,不再接收新任务,也不处理队列中的任务,并中断正在执行的任务。
  • TIDYING:整理状态,所有任务已终止,线程数为 0。
  • TERMINATED:终止状态,线程池完全关闭。

三、线程池的底层实现

1. Worker 类

ThreadPoolExecutor 使用 Worker 类封装线程和任务。Worker 实现了 Runnable 接口,其 run() 方法会不断从任务队列中获取任务并执行。

private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    final Thread thread;
    Runnable firstTask;
    Worker(Runnable firstTask) {
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }
    public void run() {
        runWorker(this);
    }
    // ...
}

2. runWorker 方法

runWorker 方法是线程执行任务的核心逻辑:

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // 允许中断
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // 如果线程池正在停止,确保线程被中断
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                try {
                    task.run();
                    afterExecute(task, null);
                } catch (Throwable ex) {
                    afterExecute(task, ex);
                    throw ex;
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}
  • getTask():从任务队列中获取任务。
  • processWorkerExit:处理线程退出逻辑。

四、总结

通过对线程生命周期和 ThreadPoolExecutor 的深入分析,我们可以更好地理解多线程编程的核心机制。线程池通过复用线程、管理任务队列和动态调整线程数,显著提升了系统的性能和稳定性。在实际开发中,合理配置线程池参数(如核心线程数、任务队列类型和拒绝策略)是优化高并发系统的关键。

对于北京互联网大厂的高并发场景,深入掌握线程池的底层实现和调优技巧,能够帮助开发者在性能与资源之间找到最佳平衡点,为系统的稳定运行提供有力保障。


希望本文能为大家提供有价值的参考,欢迎在评论区交流讨论!

相关文章:

  • vue3除了pinia/vuex的其他通讯方式还有那些
  • AWVS(web)扫描器安装与使用
  • 记一次项目上vCenter集群恢复过程
  • 网络应用层之HTTPS
  • kubernetes-完美下载
  • java进阶1——JVM
  • 从0开始的操作系统手搓教程14——进一步完成中断子系统
  • 网络原理---HTTP/HTTPS
  • 在 MySQL 的 InnoDB 存储引擎中,部分数据库优化策略
  • 当AI搜索撕开传统搜索的裂缝,警惕AI搜索的“信息茧房”
  • LINUX、WIN32、MACOSX多个平台上GCC、MSVC之间结构体按序列大小1字节对齐
  • 《Elasticsearch实战:从零开始构建高效全文搜索引擎》
  • Webpack打包过程中如何处理ES6模块的循环依赖?
  • 基于SpringBoot的“古城景区管理系统”的设计与实现(源码+数据库+文档+PPT)
  • 力扣(leetcode)每日一题 1656 设计有序流
  • 2025 年 Java 面试高频手写代码题(附核心实现)
  • 本地部署deepseek大模型后使用c# winform调用(可离线)
  • 某查”平台请求头反爬技术解析与应对
  • css selector
  • docker部署go简单web项目(无mysql等附加功能)
  • perl网站建设/搜索引擎优化是什么
  • 中国建设部网站/线下营销推广方式有哪些
  • 个人备案网站百度收录/河北网站优化公司
  • 自己怎么做视频网站/长春seo外包
  • 建站之星平台/seo排名优化工具
  • 做h网站/网站的优化与推广分析