惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
G
GRAHAM CLULEY
Attack and Defense Labs
Attack and Defense Labs
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tor Project blog
T
Threat Research - Cisco Blogs
阮一峰的网络日志
阮一峰的网络日志
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Vercel News
Vercel News
Google DeepMind News
Google DeepMind News
U
Unit 42
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Spread Privacy
Spread Privacy
C
CXSECURITY Database RSS Feed - CXSecurity.com
量子位
T
The Blog of Author Tim Ferriss
Project Zero
Project Zero
Webroot Blog
Webroot Blog
雷峰网
雷峰网
C
Cyber Attacks, Cyber Crime and Cyber Security
Microsoft Azure Blog
Microsoft Azure Blog
Microsoft Security Blog
Microsoft Security Blog
Scott Helme
Scott Helme
T
The Exploit Database - CXSecurity.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
A
About on SuperTechFans
NISL@THU
NISL@THU
AWS News Blog
AWS News Blog
Security Latest
Security Latest
S
Schneier on Security
W
WeLiveSecurity
K
Kaspersky official blog
有赞技术团队
有赞技术团队
Cyberwarzone
Cyberwarzone
P
Palo Alto Networks Blog
TaoSecurity Blog
TaoSecurity Blog
G
Google Developers Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Schneier on Security
Schneier on Security
博客园_首页
博客园 - 司徒正美
Application and Cybersecurity Blog
Application and Cybersecurity Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Check Point Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Know Your Adversary
Know Your Adversary
P
Privacy & Cybersecurity Law Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"

李锋镝的博客

LiteLLM 本地代理搭建 Claude-HUD 使用文档 Kratos+ —— Kratos 主题二次开发记录 译文:如何将单体应用拆解为微服务 codebase-memory-mcp 极简完整使用指南 Claude Haiku 4.5、Claude Sonnet 4.6、Claude Opus 4.7 区别以及各自的新特性 SchedulingConfigurer详解 踩坑60+次后,我终于搞懂 Claude Skill 怎么写才会真的触发 Everything Claude Code 详细使用文档 配置Jackson使用字段而不是getter/setter来序列化和反序列化 这个域名注册整整十年了,十年时间,真快啊 Claude Code全维度实战指南:从入门到精通,解锁AI编程新范式 Apollo配置中心中的protalDB的作用是什么 org.apache.ibatis.plugin.Interceptor类详细介绍及使用 岁末 Excel2016右键新建工作表,打开时提示“因为文件格式或文件扩展名无效。请确定文件未损坏,并且文件扩展名与文件的格式匹配。”的解决办法 wordpress增加说说功能 玩博客的人是不是越来越少了? Java 为什么有这么多 “O”? 2024年11月1号 农历十月初一 我的第一个WordPress插件:Dylan Custom Plugin上线了 推荐一款比较养眼的Xshell配色方案 hnswlib installation failed 开工啦~ 阳了... MybatisCodeHelperPro激活 @Async注解的坑 新买的笔记本发货啦…… 这个中秋节感觉过的好累啊 IDEA下载源码报:Cannot connect to the Maven process. Try again later. RocketMQ的push消费方式实现详解 看病难~取药难~~ 笑死、腹肌……根本不可能有腹肌的~~ 居家办公了~ C# 11 的这个新特性,我愿称之最强! IntelliJ IDEA 2020.3.x永久白嫖(Windows/Mac) IDEA无限试用方法【2020.3最新亲测有效】 SpringBoot使用注解的方式构建Elasticsearch查询语句,实现多条件的复杂查询 UUID太长怎么办?快来试试NanoId 忽然发现,在校大学生可以免费领一年有道云笔记会员~ 醒醒~补个税了 使用itext和freemarker来根据Html模板生成PDF文件,加水印、印章 来来来,用python画一个冰墩墩儿 SpringBoot整合GraphQL入门教程 办理居住证困难重重啊! 妈呀,昨天晚上睡觉做了一晚上的梦,可累死我了 感觉Typecho很简洁啊…… WordPress的自动更新好烦啊 BeanCopier工具类(性能优化工具类) 内存屏障浅析 居住证可算是申请通过了…… 试了下壁挂炉供暖 关于重阳节 居住证签注... Spring Boot 2.x使用PostgreSQL数据库 十一节后开工头一天,修了个耳机…… 玉楼春·尊前拟把归期说 nginx反向代理配置去除前缀 1931→2021! Navicat Premium数据库账号密码解密 百度的索引量数据现在有点儿太夸张了吧? 哇塞~这个小姐姐实在太惊艳了…… 关于8月29号下午博客故障的一些记录 如何形象的描述反应式编程中的背压(Backpressure)机制? 基于Java8的Either类 海琴烟~~~ JMX监控权限认证配置 IntelliJ IDEA 2019.3.3 永久激活 破解[Windows] SpringBoot整合Elasticsearch详细步骤以及代码示例(附源码) SpringBoot整合Elasticsearch游标查询(scroll) GitLab创建新项目,初次提交命令和流程 吐槽下Google浏览器~ 从零搭建Spring Cloud Gateway网关(一) 从零搭建Spring Cloud Gateway网关(二)—— 打印请求响应日志 博客有logo啦 你的答案 数据库事务的一点简单总结 Spring Boot发展史(Spring Boot介绍) 【漫画】戏说外行对程序员的误会有多深 【收藏】从面试官角度观察到的程序员技能瓶颈,同时给出突破瓶颈的建议 Git中.gitignore文件不起作用的解决办法 背影 - 朱自清 jmap命令(jdk1.8) 彻底搞懂mysql日志系统binlog,redolog,undolog 出院了~~~ linux中ftp查看不到文件列表的问题 PHP版本怎么更新啊…… 成语接龙 StarUML4.0破解文件 Java SPI详解 WordPress评论框增加自定义表情 因在公司上不正经网站,我没过试用期… 优化了MYSQL大量写入问题,老板奖励了1000块给我 不慌不忙的坚强(林徽因39段最美文字!) ConcurrentHashMap常用方法源码解析(jdk1.8) 我是如何失去团队掌控的? HBASE填坑日志 关闭apache httpclient4.5 DEBUG日志 使用OpenShift搭建k8s集群 SonarQube Scanner的配置与使用简介
别再背线程池的七大参数了,现在面试官都这么问
李锋镝 · 2025-05-16 · via 李锋镝的博客

当你在面试中流畅地背出线程池的七大参数时,面试官微微一笑,抛出一个灵魂拷问:"那你说说线程池是怎么实现核心线程保活的?非核心线程超时销毁时怎么保证不误杀正在执行任务的线程?" 此时你突然意识到,机械记忆参数的年代早已过去,现在面试官更关注参数背后的设计思想和源码层面的实现逻辑。本文将带你直击线程池最核心的机制,用源码告诉你为什么参数要这样设计。

一、从医院分诊系统理解线程池本质

类比说明

  • corePoolSize:常驻值班医生(核心线程)
  • maximumPoolSize:最大可调动的医生总数(含临时抽调)
  • workQueue:候诊区座位(任务队列)
  • handler:当候诊区爆满时的处理策略(拒诊/转院等)

核心逻辑:动态资源调度算法,通过源码实现机制解析线程池运作。

二、Worker的生命周期(源码级解析)

1. Worker的诞生:addWorker()

// ThreadPoolExecutor.java
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 状态检查:线程池是否已关闭等
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN && firstTask == null))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            // 关键判断:是否超过核心数/最大线程数
            if (wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // CAS失败后重试...
        }
    }

    Worker w = new Worker(firstTask);
    Thread t = w.thread;
    workers.add(w);
    t.start(); // 启动新线程
    return true;
}

设计亮点

  • 使用CAS保证线程数增减的原子性
  • 通过core参数区分核心/非核心线程
  • 维护workers集合管理所有Worker

2. Worker的生存之道: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(); // 加锁保证任务执行不被干扰
            // 处理线程中断状态...
            try {
                beforeExecute(wt, task);
                task.run(); // 执行任务
                afterExecute(task, null);
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly); // Worker退出处理
    }
}

关键机制

  • 循环取任务:通过getTask()实现任务获取
  • 可中断设计:unlock()后允许线程被回收
  • 异常处理:保证Worker异常退出时资源回收

三、灵魂方法getTask()的玄机

private Runnable getTask() {
    boolean timedOut = false; 
    for (;;) {
        int c = ctl.get();
        // 状态检查(线程池是否已停止)...

        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take(); // 核心线程在此阻塞
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

面试必考点

  • 核心线程保活:默认使用take()无限阻塞(非自旋)
  • 超时控制:非核心线程使用poll(keepAliveTime)
  • 状态联动:当线程池状态变更时,通过中断唤醒阻塞线程

四、状态机设计(CTL魔数解析)

核心结构:使用AtomicInteger同时保存线程池状态(高3位)和工作线程数(低29位)。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

// 状态定义
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

设计精妙之处

  • 单变量原子操作保证状态一致性
  • 位运算提升判断效率(比对象锁更轻量)
  • 状态转换严格有序(RUNNING -> SHUTDOWN -> STOP -> TIDYING -> TERMINATED

五、动态参数调整的陷阱

public void setCorePoolSize(int corePoolSize) {
    if (corePoolSize < 0)
        throw new IllegalArgumentException();
    int delta = corePoolSize - this.corePoolSize;
    this.corePoolSize = corePoolSize;
    if (workerCountOf(ctl.get()) > corePoolSize)
        interruptIdleWorkers(); // 中断空闲线程
    else if (delta > 0) {
        int k = Math.min(delta, workQueue.size());
        while (k-- > 0 && addWorker(null, true)) { // 补充核心线程
            if (workQueue.isEmpty())
                break;
        }
    }
}

重要启示

  • 参数修改可能触发线程中断/创建
  • 需要与当前任务队列状态联动
  • 不是简单的赋值操作,而是完整的资源调度

六、高频面试题破解示例

Q1:为什么核心线程默认不会超时销毁?

源码级回答
getTask()方法中,当判断当前是核心线程(workerCount <= corePoolSize)且未开启allowCoreThreadTimeOut时,会调用workQueue.take()无限阻塞。这种阻塞是通过LockSupport.park()实现的系统级等待,不消耗CPU资源,且只有在队列有新元素时才会被唤醒。

Q2:如何保证关闭线程池时不丢失任务?

设计思想解析

  • shutdown():将状态改为SHUTDOWN,中断所有空闲Worker,继续执行队列中的剩余任务。
  • shutdownNow():将状态改为STOP,立即中断所有Worker,并返回未处理的任务列表。
    关键区别:状态机的转换逻辑和中断策略的选择。

七、线程池设计的哲学启示

  1. 空间换时间:通过任务队列缓存请求,提高吞吐量。
  2. 惰性创建:不到万不得已不创建新线程(符合addWorker逻辑)。
  3. 优雅降级:拒绝策略本质是系统保护机制。
  4. 状态驱动:所有行为都围绕状态机展开。

结语

当你真正理解了Worker如何通过AQS实现不可重入锁、状态机转换如何影响任务调度、阻塞队列与线程存活的精妙配合,七大参数将不再是孤立的概念,而是有机组合的设计元素。这才是面试官真正想考察的——对并发编程本质的理解能力。下次面试时,不妨主动反问:"您是想了解参数设计的trade-off,还是具体的实现机制?" 这会让面试官眼前一亮。

除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/article/4399