臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
个人项目
personal(个人展示)GitHub Actions 自动部署个人展示项目
基于Quartz搭建的个人博客1.Quartz个人博客使用教程2.使用 rsync 增量部署 Quartz 博客3.使用 GitHub Actions 自动部署 Quartz 博客5.域名绑定6.CDN加速7.Google 搜索收录排查:为什么关键词搜得到别人,却搜不到我的网站?Obsidian、双向链接与知识图谱
架构设计
分布式分布式事务RPC原理
旅行
行程攻略青岛三日游行程攻略
前端
NginxNginx配置与反向代理入门
Nodenpm与npx的区别
数据库
mysql01.MySQL基础语法与约束02.MySQL数据库类型03.MySQL多表查询04.MySQL 索引05.MySQL 事务06.MySQL 隔离级别07.MySQL MVCC08.MySQL 锁09.EXPLAIN10.慢查询与 SQL 优化
mysql面试题01.索引问题02.事务与隔离级别03.锁机制04.存储引擎与性能优化05.用户权限、字符集与备份恢复
mysql实战01.EXPLAIN实战02.测试环境菜单误删排查与恢复03.MySQL IN 详解
网络
网络安全1.无线监听网卡苹果M系列虚拟机安装Kali Linux教程3.kali怎么使用RTL8812AU4.kali基础命令
网络基础进程之间怎样通过网络传递数据
网址常用网址
中间键
esElasticsearch 入门
mq
RabbitMQRabbitMQ高级RabbitMQ基础
1. MQ 基础2. MQ 核心角色与代码对应3. MQ Consumer Group 与消费模式4. SOFAMQ 账户变更代码实践5. MQ 可靠性问题6. 消息幂等
redis缓存击穿、穿透与雪崩Redis 分布式锁Redis 缓存问题Redis 数据结构
AI
使用说明
第三方插件&技能简介Archify使用与安装指南geekgeekRun使用OfferFlow使用指南Ponytail使用指南Skills Manager使用指南
CodexCodex CLI与IDE区别及使用指南Codex Hook单独配置与提交通知Codex MCP安装与使用指南Codex第三方插件安装与使用指南
Agent开发0-学习路线1.LLM、Token、上下文窗口与模型参数2.大模型 API、请求参数与响应结构3.Spring AI ChatClient4.Prompt、System Prompt、Prompt 模板6.结构化输出、JSON Schema7.SSE 流式响应8.会话 ID、聊天记录、Redis9.超时、重试、限流、降级10.*完成可运行聊天接口11.Function Calling、Tool Calling 原理12.用 Java 方法定义 Tool13.工具参数设计和校验14.实现订单查询工具15.实现物流查询工具16.实现库存查询工具17.多个工具自动选择18.工具异常、超时、重试
diy主机
主机零件学习1.CPU2.主板3.内存4.显卡5.硬盘6.散热7.风扇8.机箱热门型号价格表
捡垃圾diy-13600kf+5060diy-14600kf+5070
English高频词汇英文基础语法简单句子音标
GitGit常用命令Sublime Merge 使用指南
Java
工具库Caffeine本地缓存Reactor教程
工作实践如何使用@Async线程池使用与排查线上服务 CPU 使用率过高排查压测下RPC接口大量失败排查记录
基础1.HashMap2.ConcurrentHashMap3.HashSet4.final5.static6.volatile7.AtomicInteger8.String动态代理反射锁与线程同步线程注解
面试题1.HashMap 存储在方法内是线程安全的吗2.ArrayList参数传递与引用重新赋值3.== 和 equals 的区别、hashcode4.重载和重写的区别5.String、StringBuffer、StringBuilder区别及使用场景6.接口和抽象类7.ArrayList 与 LinkedList 的区别和扩容机制8.HashMap 和 Hashtable 的区别及底层实现9.Object 和 Objects 的区别10.ThreadLocal的原理和使用场景11.字节码是什么及作用12.Java类加载器有哪些13.双亲委派模型14.Java异常体系15.GC是什么16.GC如何判断对象可以被回收
面试总结0.HR面试模板1.面试一2.面试二3.面试三笔试
网络与 IO(黑马程序员-纯理论)01-网络编程基础02-IO模型与零拷贝03-BIO04-NIO基础与缓冲区05-NIO通道与选择器06-AIO
juc00-目录01-进程与线程02-线程同步与通信03-并发设计模式04-Java内存模型05-CAS与原子类06-ThreadLocal07-阻塞队列08-线程池使用与原理09-任务调度10-ForkJoin与享元模式11-AQS12-ReentrantLock13-读写锁14-并发协作工具15-ConcurrentHashMap16-CopyOnWrite与同步容器17-SkipListMap18-非阻塞队列
juc(工作常见)Java并发编程三个重点拆解
jvm1. JVM运行时数据区
Spring1.Spring IoC、DI与构造方法注入CGLIB、Byte Buddy 与 Spring AOPSpring 常用注解详解Spring Bean 生命周期
Spring MVC异常处理机制
linux常用命令
mac快捷键终端常用操作
python
基础01.Python是什么,为什么要安装Python02.Python项目中.venv是什么03.Python基础语法
基础实战sql替换
HTTPXHRMS员工详情接口调用(Python HTTPX)
windowswindows激活

一、这次问了什么

  1. 你们项目用到线程池了吗?主要用在哪?
  2. 金价实时变化,客户想在金价低于自己设定的价格时自动买入,你怎么设计?

二、线程池:就像提前安排好几个工作人员

假设要给 1,000 个员工发送通知。

一个人挨个发,比较慢;一下找 1,000 个人同时发,又可能把系统压垮。

线程池就像提前安排好几个工作人员:有人空下来,就从排队的任务中拿一个继续做。这样既能同时干活,又能控制人数。

项目里一般用在哪

  • 批量处理数据:比如一批员工数据,分成几份同时处理。
  • 同时查几个接口:如果几个查询互不依赖,可以一起查,最后把结果合起来。
  • 导出报表、发送通知:这些事情比较耗时,可以交给后台处理,让用户先看到“处理中”。

这些是常见例子,要选自己项目里真正做过的来讲。

面试时怎么说

用过,主要用在【自己的实际业务】。比如批量处理数据,如果一条条处理会比较慢,我们就把任务分批,交给线程池同时处理。但不能无限制地开线程,否则数据库或者下游接口可能扛不住,所以会限制同时执行的数量。处理失败的任务也会记录下来,方便重试和排查。

如果继续追问,先记住三个问题:

  1. 人够不够? 线程太少处理慢,太多又可能把系统压垮,需要实际测试。
  2. 排队的人太多怎么办? 队列要有上限,满了要有明确处理,重要任务不能直接丢。
  3. 系统重启怎么办? 重要任务先存进数据库或可靠的消息队列,不能只放在内存里。

三、金价自动买入:先用一个例子想明白

假设客户小王设置:

金价低于 600 元/克时,帮我买 10 克,只买一次。

这里的数字只是方便理解,暂不考虑手续费。

系统要做的事情就是:记住小王的要求,盯住金价,符合条件就帮他买,最后告诉他结果。

第一步:把小王的要求存下来

数据库里记一条购买要求:

谁要买低于多少买买多少当前情况
小王600 元/克10 克等待购买

还可以记录什么时候到期,以及小王有没有取消。

这里先跟面试官确认一句:“我先按低于目标价就买,而且只买一次来设计,可以吗?”

第二步:获取最新金价,看看谁满足条件

最好由提供金价的平台在价格变化时主动通知我们;如果平台只能提供查询接口,就定时去查询,但可能错过短暂出现的低价。

例如金价从 605 元降到 598 元:

  • 小王要求低于 600 元,满足条件。
  • 小李要求低于 590 元,不满足,继续等。

如果客户很多,可以按目标价格整理这些购买要求,找出满足条件的那一批,避免每次都检查所有客户。

另外,小王刚设置时如果已经是 598 元,也应该立刻检查一次,不能非等到下一次价格变化。

第三步:先标记“正在买”,防止大家重复买

假设两台服务器同时发现小王可以购买,不能各自都帮他买 10 克。

可以规定:只有成功把“等待购买”改成“正在购买”的那台服务器,才能继续。 数据库要保证这次修改只能有一个成功,不能两台服务器都先查一遍,再各自修改。

同时,把接下来要执行的购买任务存下来。这样即使服务器重启,也能知道这笔购买还没处理完。

面试官如果问专业名词,这就是用“条件更新”争取处理资格,再通过保存任务避免中途丢失。

第四步:真正买之前,再看价格和余额

刚才看到 598 元,不代表现在还能按 598 元买。

假设准备付款时涨到了 602 元,就不应该直接买。要确认交易平台能保证按符合小王要求的价格成交,比如支持限价单,或者能暂时锁住报价。

只在自己系统里查一下价格,然后直接按市价下单,仍然可能买贵。

资金也一样:不能只看一眼余额够不够,还要先把本次购买需要的钱锁住,防止同时被其他订单花掉。买成功后扣款,明确失败后再退回可用余额。

第五步:记录购买结果

  • 成功了:记下订单和成交价格,把要求改成“已购买”,通知小王。
  • 明确失败了:记下原因,释放锁住的钱,再根据规则决定是否继续等待。
  • 一直没收到结果:先记成“结果确认中”,去交易平台查,不能马上再买一遍。

最后一种最容易被追问:

就像付款时页面转圈,你不能因为页面没反应,就认定没付成功,再付一次。

每次购买都带同一个固定编号。再次查询或重试时沿用这个编号,让交易平台知道“还是刚才那笔”,并且需要平台支持按这个编号防止重复下单。这种“重复请求也只买一次”的处理,就叫幂等。

四、线程池在这里干什么

假设金价突然下跌,1,000 个客户同时满足条件。

不需要为每个客户一直开一个线程盯价格。系统统一收到价格后,找到这 1,000 笔购买任务,再安排固定数量的线程处理,剩下的先排队。

就像店里突然来了很多订单:工作人员数量有限,接一单、做一单,不能一下子无限加人。

但买黄金的任务很重要,要先保存任务,再安排线程去做。线程池负责控制同时做多少件事,数据库或可靠的消息队列负责把没做完的事记住。

五、面试时可以直接这样回答

我会先把客户设置的目标价、购买数量和是否只买一次存下来。系统收到最新金价后,找出符合条件的客户。

比如客户设置低于 600 元买 10 克,价格到了 598 元,就准备执行。执行前先把这条要求从“等待购买”改成“正在购买”,而且只允许一台服务器修改成功,防止重复买。购买任务也要保存,避免系统重启后丢失。

真正下单时,再确认能不能按客户要求的价格成交,以及余额是否足够。如果平台支持限价或锁价,就利用它保证成交价格。

购买成功后记录结果并通知客户。如果接口超时,先查询这笔订单到底成功没有,不能直接再买一次。每笔购买使用固定编号,配合交易平台的防重复能力,避免重复下单。

如果同时满足条件的客户很多,就用线程池分批处理,控制并发,避免把系统和交易接口压垮。

六、先记住这五句话

  1. 存要求:谁要买、多少钱买、买多少。
  2. 看金价:价格满足条件,就准备执行。
  3. 别重复买:同一条购买要求,只让一个任务执行。
  4. 别买贵了:看到低价不等于能按低价成交,要让交易平台保证价格。
  5. 没回消息先查结果:超时不等于失败,不能直接再买一次。