线程池详解:从 7 大参数到生产避坑

线程池详解:从 7 大参数到生产避坑

并发编程里最常被问、也最容易写错的就是线程池。很多人背得出 7 大参数的名字,却答不出"为什么加任务先进队列而不是先扩线程""拒绝策略怎么选""生产上到底怎么配"。这篇把线程池从参数到执行流程到生产避坑一次讲透。


一、结论先行

线程池的本质是"复用线程 + 排队任务":它把"创建线程的昂贵开销"变成"一次性投入",把"突发的任务洪峰"变成"可控的排队"。

理解线程池只需抓住一条主线------任务进来后的去向顺序

核心线程跑 → 核心满了进队列排队 → 队列满了才扩到最大线程 → 最大也满了走拒绝策略

四个关键结论先记住:

  1. 扩线程是最后手段,不是第二选择------队列是缓冲,线程是资源
  2. 7 大参数里,workQueuehandler 选错 = 线程池慢性死亡
  3. 生产禁用 Executors 快捷方法(无界队列会 OOM)
  4. 核心线程数没有万能公式,只有"按任务类型 + 压测校准"两条路

二、类比理解:银行柜台

把线程池想象成一家银行网点:

线程池要素 银行类比
核心线程数 corePoolSize 常驻柜员(3 个窗口常年开着)
任务队列 workQueue 大厅的等候区(凳子)
最大线程数 maximumPoolSize 网点最多能开的窗口数(含临时加开)
keepAliveTime 临时窗口空闲多久就撤
拒绝策略 handler 等候区也坐满了,怎么办
线程工厂 threadFactory 柜员的工牌(线程名/是否为守护线程)

关键场景:柜台高峰来了(任务突发)------

  1. 先让常驻 3 个柜员处理
  2. 3 个全忙 → 客户到等候区排队(进队列),而不是立刻加开窗口
  3. 等候区也满了 → 才加开临时窗口(扩到最大线程数)
  4. 临时窗口也忙、等候区还满 → 只能拒客(拒绝策略)

为什么"队列满了才扩线程"而不是"核心满了就扩"?因为线程是稀缺资源(每条线程都有栈内存、要调度),而排队只是让任务等一会儿。银行宁可让客户排队等 3 个柜员,也不会客户一来就开 20 个窗口------开窗要成本。


三、深挖细节

3.1 7 大参数逐个拆

java 复制代码
new ThreadPoolExecutor(
    corePoolSize,      // ① 核心线程数:常驻,默认不回收
    maximumPoolSize,   // ② 最大线程数:上限
    keepAliveTime,     // ③ 空闲保活时间:超过 core 的线程空闲多久被回收
    TimeUnit.SECONDS,  // ④ 时间单位
    workQueue,         // ⑤ 任务队列:核心满了先排这里
    threadFactory,     // ⑥ 线程工厂:给线程命名、设守护标志
    handler            // ⑦ 拒绝策略:队列+线程全满怎么办
);

① 核心线程数: 最常被问"怎么定"。没有万能公式,两条路:

  • 按任务类型估:CPU 密集 ≈ CPU 核数 + 1;IO 密集 ≈ CPU 核数 × 2(因为 IO 等待时不占 CPU,可以多开线程利用等待时间)
  • 压测校准:估算只是起点,上线前用压测找到吞吐/延迟的拐点,才是终值

⑤ 任务队列: 三种队列,选错致命:

队列 特点 后果
LinkedBlockingQueue 无界(默认) 任务无限堆积 → OOM
ArrayBlockingQueue 有界 队列满才会触发扩线程/拒绝
SynchronousQueue 不存任务,直接转交线程 没有等待区,线程数很快顶到 max

⑥ 线程工厂: 最容易被忽略。不自定义,线程叫 "pool-1-thread-1",出问题都不知道是哪个池。生产必须自定义:

java 复制代码
ThreadFactory factory = new ThreadFactory() {
    private final AtomicInteger seq = new AtomicInteger(1);
    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r, "order-pool-" + seq.getAndIncrement());
        t.setDaemon(false);
        return t;
    }
};

⑦ 拒绝策略: 四种,选错影响天差地别:

策略 行为 适用
AbortPolicy(默认) 直接抛 RejectedExecutionException 不能丢任务、要快速暴露问题
CallerRunsPolicy 谁提交谁执行(退回调用线程跑) 想降速又不丢任务(天然限流)
DiscardPolicy 静默丢弃 日志/打点这类可丢任务
DiscardOldestPolicy 丢弃队头最旧任务 任务有时效性(新的比旧的重要)

3.2 执行流程:一张图记牢

#mermaid-svg-uaUxSRbylePHA99Y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uaUxSRbylePHA99Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uaUxSRbylePHA99Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uaUxSRbylePHA99Y .error-icon{fill:#552222;}#mermaid-svg-uaUxSRbylePHA99Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uaUxSRbylePHA99Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uaUxSRbylePHA99Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uaUxSRbylePHA99Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uaUxSRbylePHA99Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uaUxSRbylePHA99Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uaUxSRbylePHA99Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uaUxSRbylePHA99Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uaUxSRbylePHA99Y .marker.cross{stroke:#333333;}#mermaid-svg-uaUxSRbylePHA99Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uaUxSRbylePHA99Y p{margin:0;}#mermaid-svg-uaUxSRbylePHA99Y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uaUxSRbylePHA99Y .cluster-label text{fill:#333;}#mermaid-svg-uaUxSRbylePHA99Y .cluster-label span{color:#333;}#mermaid-svg-uaUxSRbylePHA99Y .cluster-label span p{background-color:transparent;}#mermaid-svg-uaUxSRbylePHA99Y .label text,#mermaid-svg-uaUxSRbylePHA99Y span{fill:#333;color:#333;}#mermaid-svg-uaUxSRbylePHA99Y .node rect,#mermaid-svg-uaUxSRbylePHA99Y .node circle,#mermaid-svg-uaUxSRbylePHA99Y .node ellipse,#mermaid-svg-uaUxSRbylePHA99Y .node polygon,#mermaid-svg-uaUxSRbylePHA99Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uaUxSRbylePHA99Y .rough-node .label text,#mermaid-svg-uaUxSRbylePHA99Y .node .label text,#mermaid-svg-uaUxSRbylePHA99Y .image-shape .label,#mermaid-svg-uaUxSRbylePHA99Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-uaUxSRbylePHA99Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uaUxSRbylePHA99Y .rough-node .label,#mermaid-svg-uaUxSRbylePHA99Y .node .label,#mermaid-svg-uaUxSRbylePHA99Y .image-shape .label,#mermaid-svg-uaUxSRbylePHA99Y .icon-shape .label{text-align:center;}#mermaid-svg-uaUxSRbylePHA99Y .node.clickable{cursor:pointer;}#mermaid-svg-uaUxSRbylePHA99Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uaUxSRbylePHA99Y .arrowheadPath{fill:#333333;}#mermaid-svg-uaUxSRbylePHA99Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uaUxSRbylePHA99Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uaUxSRbylePHA99Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uaUxSRbylePHA99Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uaUxSRbylePHA99Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uaUxSRbylePHA99Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uaUxSRbylePHA99Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uaUxSRbylePHA99Y .cluster text{fill:#333;}#mermaid-svg-uaUxSRbylePHA99Y .cluster span{color:#333;}#mermaid-svg-uaUxSRbylePHA99Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uaUxSRbylePHA99Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uaUxSRbylePHA99Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-uaUxSRbylePHA99Y .icon-shape,#mermaid-svg-uaUxSRbylePHA99Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uaUxSRbylePHA99Y .icon-shape p,#mermaid-svg-uaUxSRbylePHA99Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uaUxSRbylePHA99Y .icon-shape .label rect,#mermaid-svg-uaUxSRbylePHA99Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uaUxSRbylePHA99Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uaUxSRbylePHA99Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uaUxSRbylePHA99Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是





提交任务
工作线程 < 核心数?
直接创建线程执行
队列还能装?
进队列排队
工作线程 < 最大数?
创建新线程执行
走拒绝策略

注意两个反直觉点:

  • 扩线程前先排队列(不是"核心满就扩线程")------所以核心数和队列的组合决定了真正的容量
  • 新任务进队列时,不是让队列里的旧任务先跑------先判断能不能开新线程给新任务,逻辑顺序是"新任务优先尝试有线程就跑",所以执行顺序不是严格 FIFO

3.3 核心线程数为什么不能拍脑袋定

#mermaid-svg-kGJG309SxFPzZKM8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kGJG309SxFPzZKM8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kGJG309SxFPzZKM8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kGJG309SxFPzZKM8 .error-icon{fill:#552222;}#mermaid-svg-kGJG309SxFPzZKM8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kGJG309SxFPzZKM8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kGJG309SxFPzZKM8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kGJG309SxFPzZKM8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kGJG309SxFPzZKM8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kGJG309SxFPzZKM8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kGJG309SxFPzZKM8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kGJG309SxFPzZKM8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kGJG309SxFPzZKM8 .marker.cross{stroke:#333333;}#mermaid-svg-kGJG309SxFPzZKM8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kGJG309SxFPzZKM8 p{margin:0;}#mermaid-svg-kGJG309SxFPzZKM8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kGJG309SxFPzZKM8 .cluster-label text{fill:#333;}#mermaid-svg-kGJG309SxFPzZKM8 .cluster-label span{color:#333;}#mermaid-svg-kGJG309SxFPzZKM8 .cluster-label span p{background-color:transparent;}#mermaid-svg-kGJG309SxFPzZKM8 .label text,#mermaid-svg-kGJG309SxFPzZKM8 span{fill:#333;color:#333;}#mermaid-svg-kGJG309SxFPzZKM8 .node rect,#mermaid-svg-kGJG309SxFPzZKM8 .node circle,#mermaid-svg-kGJG309SxFPzZKM8 .node ellipse,#mermaid-svg-kGJG309SxFPzZKM8 .node polygon,#mermaid-svg-kGJG309SxFPzZKM8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kGJG309SxFPzZKM8 .rough-node .label text,#mermaid-svg-kGJG309SxFPzZKM8 .node .label text,#mermaid-svg-kGJG309SxFPzZKM8 .image-shape .label,#mermaid-svg-kGJG309SxFPzZKM8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-kGJG309SxFPzZKM8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kGJG309SxFPzZKM8 .rough-node .label,#mermaid-svg-kGJG309SxFPzZKM8 .node .label,#mermaid-svg-kGJG309SxFPzZKM8 .image-shape .label,#mermaid-svg-kGJG309SxFPzZKM8 .icon-shape .label{text-align:center;}#mermaid-svg-kGJG309SxFPzZKM8 .node.clickable{cursor:pointer;}#mermaid-svg-kGJG309SxFPzZKM8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kGJG309SxFPzZKM8 .arrowheadPath{fill:#333333;}#mermaid-svg-kGJG309SxFPzZKM8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kGJG309SxFPzZKM8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kGJG309SxFPzZKM8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kGJG309SxFPzZKM8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kGJG309SxFPzZKM8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kGJG309SxFPzZKM8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kGJG309SxFPzZKM8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kGJG309SxFPzZKM8 .cluster text{fill:#333;}#mermaid-svg-kGJG309SxFPzZKM8 .cluster span{color:#333;}#mermaid-svg-kGJG309SxFPzZKM8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kGJG309SxFPzZKM8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kGJG309SxFPzZKM8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-kGJG309SxFPzZKM8 .icon-shape,#mermaid-svg-kGJG309SxFPzZKM8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kGJG309SxFPzZKM8 .icon-shape p,#mermaid-svg-kGJG309SxFPzZKM8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kGJG309SxFPzZKM8 .icon-shape .label rect,#mermaid-svg-kGJG309SxFPzZKM8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kGJG309SxFPzZKM8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kGJG309SxFPzZKM8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kGJG309SxFPzZKM8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CPU 密集
IO 密集
任务类型
CPU 密集 or IO 密集
≈ CPU 核数 + 1
≈ CPU 核数 × 2 或更高
压测校准

  • CPU 密集(计算为主):线程多了反而竞争 CPU,核数 + 1 足够
  • IO 密集(等网络/等数据库):线程在等 IO 时 CPU 空闲,多开线程把 CPU 用满
  • 真实系统几乎都是混合,所以最终靠压测:观察拐点------线程加到某个数后,吞吐不再涨甚至下降,就是最优值附近

3.4 生产避坑清单(每一条都是血泪)

现象 解法
Executors.newFixedThreadPool 无界队列,任务堆积 OOM 手动 new,有界队列
Executors.newCachedThreadPool SynchronousQueue,突发任务瞬间创建海量线程 手动限 max
任务里嵌套提交给同池 子任务永远排不进队列 → 线程池死锁 拆池或用不同队列
线程池里用 ThreadLocal 不清理 线程复用导致脏数据串到下一个任务 用后 remove
不给线程命名 线上看不到是哪个池出问题 自定义 ThreadFactory
线程池参数写死 业务量变化后无法调整 用 setCorePoolSize 支持动态调整

"任务里嵌套提交子任务"值得细说 :任务 A 在运行中又向同一个池提交任务 B,A 占着一个核心线程等 B 完成,B 排在队列里等线程------如果核心线程都被这种 A 占满,队列里的 B 永远等不到线程,整个池子假死。这是典型的自锁,必须拆成两个池或用不同的队列隔离。

3.5 参数能不能动态调

能。核心线程数和最大线程数都支持运行时调整:

  • setCorePoolSize():热更新核心数
  • setMaximumPoolSize():热更新上限

所以生产上参数不必一次定死,配合监控(队列深度、活跃线程数、拒绝次数)动态调整才是正解。拒绝次数 > 0 说明容量不够要扩;队列长期空说明线程开多了要缩。


四、梳理成一个知识点

#mermaid-svg-vNPnx08hgmVoB9dV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vNPnx08hgmVoB9dV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vNPnx08hgmVoB9dV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vNPnx08hgmVoB9dV .error-icon{fill:#552222;}#mermaid-svg-vNPnx08hgmVoB9dV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vNPnx08hgmVoB9dV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vNPnx08hgmVoB9dV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vNPnx08hgmVoB9dV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vNPnx08hgmVoB9dV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vNPnx08hgmVoB9dV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vNPnx08hgmVoB9dV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vNPnx08hgmVoB9dV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vNPnx08hgmVoB9dV .marker.cross{stroke:#333333;}#mermaid-svg-vNPnx08hgmVoB9dV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vNPnx08hgmVoB9dV p{margin:0;}#mermaid-svg-vNPnx08hgmVoB9dV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vNPnx08hgmVoB9dV .cluster-label text{fill:#333;}#mermaid-svg-vNPnx08hgmVoB9dV .cluster-label span{color:#333;}#mermaid-svg-vNPnx08hgmVoB9dV .cluster-label span p{background-color:transparent;}#mermaid-svg-vNPnx08hgmVoB9dV .label text,#mermaid-svg-vNPnx08hgmVoB9dV span{fill:#333;color:#333;}#mermaid-svg-vNPnx08hgmVoB9dV .node rect,#mermaid-svg-vNPnx08hgmVoB9dV .node circle,#mermaid-svg-vNPnx08hgmVoB9dV .node ellipse,#mermaid-svg-vNPnx08hgmVoB9dV .node polygon,#mermaid-svg-vNPnx08hgmVoB9dV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vNPnx08hgmVoB9dV .rough-node .label text,#mermaid-svg-vNPnx08hgmVoB9dV .node .label text,#mermaid-svg-vNPnx08hgmVoB9dV .image-shape .label,#mermaid-svg-vNPnx08hgmVoB9dV .icon-shape .label{text-anchor:middle;}#mermaid-svg-vNPnx08hgmVoB9dV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vNPnx08hgmVoB9dV .rough-node .label,#mermaid-svg-vNPnx08hgmVoB9dV .node .label,#mermaid-svg-vNPnx08hgmVoB9dV .image-shape .label,#mermaid-svg-vNPnx08hgmVoB9dV .icon-shape .label{text-align:center;}#mermaid-svg-vNPnx08hgmVoB9dV .node.clickable{cursor:pointer;}#mermaid-svg-vNPnx08hgmVoB9dV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vNPnx08hgmVoB9dV .arrowheadPath{fill:#333333;}#mermaid-svg-vNPnx08hgmVoB9dV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vNPnx08hgmVoB9dV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vNPnx08hgmVoB9dV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vNPnx08hgmVoB9dV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vNPnx08hgmVoB9dV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vNPnx08hgmVoB9dV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vNPnx08hgmVoB9dV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vNPnx08hgmVoB9dV .cluster text{fill:#333;}#mermaid-svg-vNPnx08hgmVoB9dV .cluster span{color:#333;}#mermaid-svg-vNPnx08hgmVoB9dV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vNPnx08hgmVoB9dV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vNPnx08hgmVoB9dV rect.text{fill:none;stroke-width:0;}#mermaid-svg-vNPnx08hgmVoB9dV .icon-shape,#mermaid-svg-vNPnx08hgmVoB9dV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vNPnx08hgmVoB9dV .icon-shape p,#mermaid-svg-vNPnx08hgmVoB9dV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vNPnx08hgmVoB9dV .icon-shape .label rect,#mermaid-svg-vNPnx08hgmVoB9dV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vNPnx08hgmVoB9dV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vNPnx08hgmVoB9dV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vNPnx08hgmVoB9dV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 线程池
7 参数: 核队时大工队列拒
流程: 核跑-排队-扩线-拒绝
队列是缓冲 线程是资源
核数按类型估+压测校准
监控: 队列深度/拒绝次数

知识点:线程池 = 核心线程复用 + 有界队列缓冲 + 最大线程兜底 + 拒绝策略收口。 执行顺序"核心→队列→最大→拒绝"决定了容量模型;workQueue 必须用有界、threadFactory 必须命名、拒绝策略按"能否丢任务"选;核心线程数按任务类型估算 + 压测校准,参数支持热更新。

口诀:核跑、队排、扩线、拒绝;队列要防无界,线程要防自锁,拒绝数 > 0 就要扩。


追问链

追问 1:"为什么先加队列而不是先扩线程?"

→ 线程是稀缺资源:每条线程有独立栈内存(默认 1MB 级)且要参与调度。任务排队只是延迟执行,代价小;无限扩线程会耗尽内存、疯狂切换导致 CPU 空转。所以"队列缓冲 + 线程兜底",扩线程是最后手段。

追问 2:"核心线程会被回收吗?"

→ 默认不会(allowCoreThreadTimeOut(true) 可以开启回收)。keepAliveTime 只对超过核心数的部分生效------临时加开的线程空闲超过保活时间会被回收,回到核心数。

追问 3:"CallerRunsPolicy 为什么能限流?"

→ 队列和线程都满时,把任务退回给提交它的线程执行。调用方线程在跑任务期间无法继续提交新任务------天然形成背压,提交速率自动降到处理速率。缺点是会阻塞调用方,要注意超时。

追问 4:"线程池的线程什么时候退出?"

→ 线程池的空闲线程靠 workQueue.poll(keepAliveTime) 等待任务------超时等不到任务就退出 。这也是为什么 keepAliveTime 只对非核心线程生效:核心线程用 take() 永久阻塞等待。

追问 5:"IO 密集 ×2 的公式还够用吗?"

→ 现代系统不够(虚拟线程/协程出现后更不够)。公式只适合传统线程模型做粗估。更准的做法是压测看吞吐拐点;Java 21 的虚拟线程让"每个任务一条虚拟线程"成为可能,线程池的适用场景正在被重新定义------但老系统排查这套公式依然要会算。

总结

线程池 = 复用线程省创建开销 + 队列缓冲抗洪峰。一条主线:核心跑→队列排→扩线程→拒绝;三个必守:有界队列防 OOM、自定义工厂防裸奔、拒绝计数当容量警报。核心线程数先按类型估(CPU 密集核数+1 / IO 密集核数×2)再用压测校准,参数可热更新。

这篇是"Agent 生产底座"的知识铺垫------Agent 系统的并发网关、批量任务处理都依赖线程池,下一篇开始讲 Agent 系统背后的 JVM:你的 Agent 服务跑在什么样的内存模型上,GC 又在什么时候卡你一刀。

相关推荐
龙亘川1 小时前
数智赋能退役军人服务:V1.0 系统搭建全生命周期闭环服务体系
大数据·数据库·人工智能
迪康Defender1 小时前
公用电脑责任追溯难?迪康端点安全一体化管理系统用户模式详解
java·运维·开发语言·网络·其他·安全
zhangguojia71 小时前
权限弹窗点击允许的授权流程
运维·服务器·数据库
jnrjian2 小时前
Postgres 序列(sequence)的权限
数据库·postgresql
风禾万里2 小时前
【无标题】
数据库
京师20万禁军教头2 小时前
47-常用类-八大包装类(Wrapper Classes)
java
镜舟科技2 小时前
为什么 Text-to-SQL 总是停在 Demo?
数据库·sql·demo·text-to-sql·镜舟科技·mip·语义视图
come112342 小时前
剪映零基础教程
java·linux·运维
16月6日-晴2 小时前
Java面向对象——接口
java·开发语言