乐于分享
好东西不私藏

线程池进阶:从参数调优到源码级解析,架构师必须懂的底层原理

线程池进阶:从参数调优到源码级解析,架构师必须懂的底层原理

面试官:”你之前说过线程池的7个参数,那你知道线程池的状态流转吗?execute()方法是怎么工作的?参数怎么调优?”

初级候选人:”呃…corePoolSize、maximumPoolSize…”

面试官:”我问的是底层原理和调优方法,不是参数名字。”

你:”线程池有5个状态:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。execute()方法的工作流程是…”

面试官:”不错,再说说参数怎么调优?监控哪些指标?”

你:”我们通过压测确定corePoolSize和maximumPoolSize,监控指标包括活跃线程数、队列深度、任务完成时间、拒绝次数。动态调整可以…”

面试官:(点头)”可以了,等通知。”

阅读本文后,你将带走:

  • 🧠 线程池状态机: 5个状态的流转图,面试加分项
  • 📋 execute()方法源码级解析: 任务提交的完整流程,工作原理
  • 🎯 参数调优实战: 监控指标、压测方法、动态调整策略
  • 💡 异常处理机制: 线程池异常、任务异常、UncaughtExceptionHandler
  • 🔥 生产环境避坑: 线程池监控、故障排查、性能优化

一、引言:为什么需要深入理解线程池?

背景

之前的文章讲清楚了线程池的7个核心参数和基本用法。但在生产环境中,只知道”corePoolSize、maximumPoolSize、workQueue”是远远不够的。

生产环境的实际问题:

  1. 线程池状态异常
    线程池卡在SHUTDOWN状态,无法接收新任务
  2. 参数配置不当
    队列容量太大,任务堆积导致响应时间暴涨
  3. 异常处理不当
    任务执行失败,线程池没有正确处理,导致业务中断
  4. 性能调优困难
    不知道如何调整参数,只能”拍脑袋”配一个值
  5. 故障排查困难
    不知道如何监控线程池,出现问题时不知道从哪里下手

核心目标:

  • ✅ 理解线程池的状态流转(面试加分项)
  • ✅ 掌握execute()方法的完整流程(理解工作原理)
  • ✅ 掌握参数调优方法(生产环境必备)
  • ✅ 掌握异常处理机制(避免生产故障)
  • ✅ 掌握监控和故障排查方法(运维必备)

二、线程池状态机:5个状态的流转图

▪ 2.1 线程池的5个状态

状态
数值
说明
是否接收新任务
是否处理队列中的任务
RUNNING
-1<<29
线程池正在运行
✅ 是
✅ 是
SHUTDOWN
0
线程池正在关闭
❌ 否
✅ 是
STOP
1
线程池已经停止
❌ 否
❌ 否
TIDYING
2
线程池正在整理
❌ 否
❌ 否
TERMINATED
3
线程池已终止
❌ 否
❌ 否

▪ 2.2 状态流转图

状态流转说明:

  1. RUNNING → SHUTDOWN

    • 调用shutdown()方法
    • 不再接收新任务
    • 继续处理队列中的任务
  2. RUNNING → STOP

    • 调用shutdownNow()方法
    • 不再接收新任务
    • 不再处理队列中的任务
    • 尝试中断正在执行的任务
  3. SHUTDOWN → TIDYING

    • 队列中的任务全部处理完成
    • 线程池中的工作线程全部为0
    • 调用terminated()钩子方法
  4. STOP → TIDYING

    • 线程池中的工作线程全部为0
    • 调用terminated()钩子方法
  5. TIDYING → TERMINATED

    • terminated()
      钩子方法执行完成

▪ 2.3 源码级解析

ThreadPoolExecutor的ctl字段(高3位存储状态,低29位存储工作线程数):

// 线程池状态(高3位)private static final int RUNNING    = -1 << COUNT_BITS;  // 11100000000000000000000000000000private static final int SHUTDOWN   =  0 << COUNT_BITS;  // 00000000000000000000000000000000private static final int STOP       =  1 << COUNT_BITS;  // 00100000000000000000000000000000private static final int TIDYING    =  2 << COUNT_BITS;  // 01000000000000000000000000000000private static final int TERMINATED =  3 << COUNT_BITS;  // 01100000000000000000000000000000// 工作线程数(低29位)private static final int COUNT_BITS = Integer.SIZE – 3;  // 32 – 3 = 29private static final int CAPACITY   = (1 << COUNT_BITS) – 1;  // 2^29 – 1// ctl字段(高3位:线程池状态,低29位:工作线程数)private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));// 获取线程池状态private static int runStateOf(int c) {    return c & ~CAPACITY;}// 获取工作线程数private static int workerCountOf(int c) {    return c & CAPACITY;}

为什么用高3位存储状态?

  1. 状态数少
    线程池只有5个状态,3位足够存储(2^3 = 8 > 5)
  2. 线程数大
    工作线程数可能很大,29位可以存储2^29个线程(约5亿个)
  3. 原子操作
    状态和线程数存储在一个AtomicInteger中,可以用一次CAS操作更新

三、execute()方法源码级解析:任务提交的完整流程

▪ 3.1 execute()方法的完整流程

public void execute(Runnable command) {    // 1. 检查任务是否为null    if (command == null)        throw new NullPointerException();    // 2. 获取ctl的值(包含状态和工作线程数)    int c = ctl.get();    // 3. 如果工作线程数 < corePoolSize,创建核心线程    if (workerCountOf(c) < corePoolSize) {        if (addWorker(command, true))  // true表示创建核心线程            return;        c = ctl.get();  // 如果创建失败,重新获取ctl的值    }    // 4. 如果线程池是RUNNING状态,尝试将任务添加到队列    if (isRunning(c) && workQueue.offer(command)) {        int recheck = ctl.get();  // 双重检查        // 4.1 如果线程池不是RUNNING状态,移除任务        if (!isRunning(recheck) && remove(command))            reject(command);  // 执行拒绝策略        // 4.2 如果工作线程数为0,创建非核心线程(消费队列中的任务)        else if (workerCountOf(recheck) == 0)            addWorker(null, false);  // false表示创建非核心线程    }    // 5. 如果队列已满,尝试创建非核心线程    else if (!addWorker(command, false))        // 5.1 如果创建失败,执行拒绝策略        reject(command);}

▪ 3.2 addWorker()方法:创建工作线程

private boolean addWorker(Runnable firstTask, boolean core) {    // 1. CAS更新工作线程数    retry:    for (;;) {        int c = ctl.get();        int rs = runStateOf(c);        // 1.1 如果线程池状态 >= STOP,不再接收新任务        if (rs >= SHUTDOWN &&            !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))            return false;        for (;;) {            int wc = workerCountOf(c);            // 1.2 如果工作线程数 >= CAPACITY 或 >= corePoolSize/maximumPoolSize,不再创建线程            if (wc >= CAPACITY ||                wc >= (core ? corePoolSize : maximumPoolSize))                return false;            // 1.3 CAS更新工作线程数            if (compareAndIncrementWorkerCount(c))                break retry;            c = ctl.get();  // 如果CAS失败,重新读取ctl的值            if (runStateOf(c) != rs)                continue retry;        }    }    // 2. 创建Worker并启动线程    boolean workerStarted = false;    boolean workerAdded = false;    Worker w = null;    try {        // 2.1 创建Worker(Worker继承自AQS)        w = new Worker(firstTask);        final Thread t = w.thread;        if (t != null) {            final ReentrantLock mainLock = this.mainLock;            mainLock.lock();            try {                // 2.2 检查线程池状态                int rs = runStateOf(ctl.get());                if (rs < SHUTDOWN ||                    (rs == SHUTDOWN && firstTask == null)) {                    // 2.3 将Worker添加到workers集合                    workers.add(w);                    int s = workers.size();                    if (s > largestPoolSize)                        largestPoolSize = s;                    workerAdded = true;                }            } finally {                mainLock.unlock();            }            // 2.4 启动线程            if (workerAdded) {                t.start();                workerStarted = true;            }        }    } finally {        // 2.5 如果启动失败,回滚        if (!workerStarted)            addWorkerFailed(w);    }    return workerStarted;}

▪ 3.3 Worker.run()方法:执行任务

private final class Worker extends AbstractQueuedSynchronizer implements Runnable {    final Thread thread;          // Worker绑定的线程    Runnable firstTask;          // 第一个任务    volatile long completedTasks;  // 完成的任务数    Worker(Runnable firstTask) {        setState(-1);  // 禁止中断,直到runWorker        this.firstTask = firstTask;        this.thread = getThreadFactory().newThread(this);    }    public void run() {        runWorker(this);    }}final void runWorker(Worker w) {    Thread wt = Thread.currentThread();    Runnable task = w.firstTask;    w.firstTask = null;  // 释放第一个任务    w.unlock();  // 允许中断    boolean completedAbruptly = true;    try {        // 1. 循环执行任务        while (task != null || (task = getTask()) != null) {            w.lock();            // 1.1 如果线程池正在停止,中断线程            if ((runStateAtLeast(ctl.get(), STOP) ||                 (Thread.interrupted() &&                  runStateAtLeast(ctl.get(), STOP))) &&                !wt.isInterrupted())                wt.interrupt();            try {                // 1.2 执行beforeExecute钩子方法                beforeExecute(wt, task);                Throwable thrown = null;                try {                    // 1.3 执行任务                    task.run();                } catch (RuntimeException x) {                    thrown = x;                    throw x;                } catch (Error x) {                    thrown = x;                    throw x;                } catch (Throwable x) {                    thrown = x;                    throw new Error(x);                } finally {                    // 1.4 执行afterExecute钩子方法                    afterExecute(task, thrown);                }            } finally {                task = null;                w.completedTasks++;                w.unlock();            }        }        completedAbruptly = false;    } finally {        // 2. Worker退出,处理工作线程数和线程池状态        processWorkerExit(w, completedAbruptly);    }}

▪ 3.4 getTask()方法:从队列中获取任务

private Runnable getTask() {    boolean timedOut = false;  // 上次poll是否超时    for (;;) {        int c = ctl.get();        int rs = runStateOf(c);        // 1. 如果线程池状态 >= STOP 或(SHUTDOWN + 队列为空),返回null        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {            decrementWorkerCount();            return null;        }        int wc = workerCountOf(c);        // 2. 判断是否需要超时控制        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;        // 2.1 如果工作线程数 > maximumPoolSize 或(需要超时控制 + 超时)        if ((wc > maximumPoolSize || (timed && timedOut))            && (wc > 1 || workQueue.isEmpty())) {            // CAS更新工作线程数            if (compareAndDecrementWorkerCount(c))                return null;        }        // 3. 从队列中获取任务        try {            // 3.1 如果需要超时控制,使用poll(timeout)            Runnable r = timed ?                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :                workQueue.take();  // 否则,使用take()阻塞            // 3.2 如果获取到任务,返回            if (r != null)                return r;            timedOut = true;  // 标记超时        } catch (InterruptedException retry) {            timedOut = false;        }    }}

四、参数调优实战:监控指标、压测方法、动态调整

▪ 4.1 线程池监控指标

核心监控指标:

指标
说明
阈值
告警级别
活跃线程数
当前正在执行任务的线程数
> maximumPoolSize
P2
队列深度
队列中等待的任务数
> 队列容量的80%
P1
任务完成时间
任务从提交到完成的平均时间
> SLA(如1秒)
P1
拒绝次数
被拒绝策略处理的任务数
> 0
P1
线程池状态
线程池的当前状态
!= RUNNING
P2

监控代码示例:

@Componentpublic class ThreadPoolMonitor {    @Autowired    private ThreadPoolExecutor executor;    @Scheduled(fixedRate = 10000)  // 每10秒输出一次    public void monitor() {        // 1. 活跃线程数        int activeCount = executor.getActiveCount();        log.info(“活跃线程数: {}/{}”, activeCount, executor.getMaximumPoolSize());        // 2. 队列深度        int queueSize = executor.getQueue().size();        int queueCapacity = executor.getQueue().remainingCapacity() + queueSize;        log.info(“队列深度: {}/{}”, queueSize, queueCapacity);        // 3. 任务完成数        long completedTaskCount = executor.getCompletedTaskCount();        log.info(“已完成任务数: {}”, completedTaskCount);        // 4. 线程池状态        log.info(“线程池状态: {}”, isExecutorShutdown() ? “SHUTDOWN” : “RUNNING”);        // 5. 拒绝策略(需要自定义拒绝策略)        log.info(“拒绝任务数: {}”, rejectedTaskCount);    }    private boolean isExecutorShutdown() {        return executor.isShutdown();    }}

使用Actuator监控:

# application.ymlmanagement:  endpoints:    web:      exposure:        include: health,info,metrics,prometheus  metrics:    tags:      application: my-app    export:      prometheus:        enabled: true

Prometheus指标:

# 线程池活跃线程数jvm_threads_live_threads{application=”my-app”}# 线程池队列大小executor_pool_queue_size{application=”my-app”,name=”taskExecutor”}# 线程池完成任务数executor_pool_completed_tasks{application=”my-app”,name=”taskExecutor”}# 线程池拒绝任务数executor_pool_rejected_tasks{application=”my-app”,name=”taskExecutor”}

▪ 4.2 压测方法

压测工具:

  1. JMeter
    图形化界面,适合HTTP接口压测
  2. Gatling
    基于Scala,性能更强,适合复杂场景
  3. K6
    基于JavaScript,轻量级,适合云原生
  4. wrk
    轻量级,适合简单压测

压测步骤:

  1. 确定压测目标

    • QPS(每秒查询数):1000 QPS
    • 响应时间:P99 < 1秒
    • 成功率:> 99%
  2. 设计压测场景

    • 并发用户数:100、200、500、1000
    • 持续时间:5分钟、10分钟、30分钟
    • 请求模式:固定并发、 ramp-up(递增并发)
  3. 执行压测

    • 单线程压测(找到单线程的QPS上限)
    • 多线程压测(找到最优的线程数)
    • 长时间压测(检查内存泄漏、连接泄漏)
  4. 分析压测结果

    • 找到QPS上限(瓶颈在哪个环节:CPU、内存、IO)
    • 找到最优参数(corePoolSize、maximumPoolSize、队列容量)
    • 找到异常情况(拒绝次数、超时次数、失败率)

▪ 4.3 参数调优策略

初始参数配置:

// CPU密集型ThreadPoolExecutor executor = new ThreadPoolExecutor(    CPU核心数 + 1,              // corePoolSize    CPU核心数 + 1,              // maximumPoolSize    60L, TimeUnit.SECONDS,       // keepAliveTime    new LinkedBlockingQueue<>(100)  // workQueue);// IO密集型ThreadPoolExecutor executor = new ThreadPoolExecutor(    CPU核心数 * 2,              // corePoolSize    CPU核心数 * 4,              // maximumPoolSize    60L, TimeUnit.SECONDS,       // keepAliveTime    new LinkedBlockingQueue<>(100)  // workQueue);

动态调整策略:

@Componentpublic class ThreadPoolDynamicTuner {    @Autowired    private ThreadPoolExecutor executor;    @Scheduled(fixedRate = 60000)  // 每60秒检查一次    public void tuneThreadPool() {        // 1. 获取监控指标        int activeCount = executor.getActiveCount();        int queueSize = executor.getQueue().size();        int completedTaskCount = executor.getCompletedTaskCount();        // 2. 动态调整corePoolSize        if (activeCount > executor.getCorePoolSize() * 0.8) {            // 活跃线程数 > corePoolSize的80%,增加corePoolSize            int newCorePoolSize = Math.min(                executor.getCorePoolSize() + 2,                executor.getMaximumPoolSize()            );            executor.setCorePoolSize(newCorePoolSize);            log.info(“增加corePoolSize: {}”, newCorePoolSize);        } else if (activeCount < executor.getCorePoolSize() * 0.3) {            // 活跃线程数 < corePoolSize的30%,减少corePoolSize            int newCorePoolSize = Math.max(                executor.getCorePoolSize() – 2,                1            );            executor.setCorePoolSize(newCorePoolSize);            log.info(“减少corePoolSize: {}”, newCorePoolSize);        }        // 3. 动态调整maximumPoolSize        if (queueSize > executor.getQueue().remainingCapacity() * 0.8) {            // 队列深度 > 剩余容量的80%,增加maximumPoolSize            int newMaximumPoolSize = Math.min(                executor.getMaximumPoolSize() + 2,                200  // 最大200            );            executor.setMaximumPoolSize(newMaximumPoolSize);            log.info(“增加maximumPoolSize: {}”, newMaximumPoolSize);        }    }}

压测调优案例:

场景1:CPU密集型任务(计算斐波那契数列)

初始配置:corePoolSize = 4(CPU核心数 + 1)maximumPoolSize = 4queueCapacity = 100压测结果:QPS = 200P99响应时间 = 800ms拒绝次数 = 0问题分析:– CPU利用率 = 100%– QPS无法提升(CPU瓶颈)优化方案:不增加线程数(CPU已经100%),优化任务逻辑(使用缓存、算法优化)

场景2:IO密集型任务(查询数据库)

初始配置:corePoolSize = 8(CPU核心数 * 2)maximumPoolSize = 16queueCapacity = 100压测结果:QPS = 500P99响应时间 = 1.5s拒绝次数 = 10问题分析:– CPU利用率 = 30%– 队列深度 = 80(任务堆积)– 响应时间 > SLA优化方案:1. 增加maximumPoolSize:16 → 322. 减小队列容量:100 → 50(快速失败)3. 使用CallerRunsPolicy(调用者线程执行)优化后:QPS = 800P99响应时间 = 900ms拒绝次数 = 0

五、异常处理机制:线程池异常、任务异常、UncaughtExceptionHandler

▪ 5.1 线程池异常

场景1:线程池关闭后提交任务

executor.shutdown();  // 关闭线程池executor.execute(() -> {    System.out.println(“Hello”);});  // RejectedExecutionException

解决方案1:检查线程池状态

if (!executor.isShutdown()) {    executor.execute(() -> {        System.out.println(“Hello”);    });}

解决方案2:使用CallerRunsPolicy拒绝策略

ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100),    new ThreadPoolExecutor.CallerRunsPolicy()  // 调用者线程执行);

▪ 5.2 任务异常

场景2:任务执行失败

executor.execute(() -> {    int i = 1 / 0;  // ArithmeticException});

问题:

  • 任务执行失败,异常被线程池吞掉,调用方不知道
  • 如果任务抛出未捕获异常,线程会终止,线程池会创建新线程

解决方案1:使用try-catch捕获异常

executor.execute(() -> {    try {        int i = 1 / 0;    } catch (Exception e) {        log.error(“任务执行失败”, e);    }});

解决方案2:重写afterExecute()方法

public class MyThreadPoolExecutor extends ThreadPoolExecutor {    @Override    protected void afterExecute(Runnable r, Throwable t) {        super.afterExecute(r, t);        if (t != null) {            log.error(“任务执行失败”, t);        }    }}

解决方案3:使用Future.get()

Future    int i = 1 / 0;});try {    future.get();  // 抛出ExecutionException,包装了ArithmeticException} catch (InterruptedException e) {    log.error(“线程被中断”, e);} catch (ExecutionException e) {    log.error(“任务执行失败”, e.getCause());}

▪ 5.3 UncaughtExceptionHandler

场景3:线程中抛出未捕获异常

executor.execute(() -> {    int i = 1 / 0;  // ArithmeticException});

问题:

  • 线程会终止
  • 线程池会创建新线程(如果工作线程数 < corePoolSize/maximumPoolSize)
  • 异常没有被记录

解决方案:设置UncaughtExceptionHandler

ThreadFactory threadFactory = r -> {    Thread thread = new Thread(r);    thread.setUncaughtExceptionHandler((t, e) -> {        log.error(“线程{}执行失败”, t.getName(), e);    });    return thread;};ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100),    threadFactory);

六、生产环境避坑

▪ 6.1 坑1:队列容量太大

// ❌ 错误配置ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new LinkedBlockingQueue<>(10000)  // 队列容量太大);

问题:

  • 高峰期任务全部堆积在队列里
  • 响应时间从100ms变成30秒
  • 内存占用暴涨(队列中堆积了10000个任务)

解决方案:

// ✅ 正确配置ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100),  // 有界队列    new ThreadPoolExecutor.CallerRunsPolicy()  // 快速失败);

▪ 6.2 坑2:不设置拒绝策略

// ❌ 错误配置ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100)    // 默认AbortPolicy,直接抛异常);

问题:

  • 任务被拒绝,调用方收到RejectedExecutionException
  • 业务流程中断

解决方案:

// ✅ 正确配置ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100),    new ThreadPoolExecutor.CallerRunsPolicy()  // 调用者线程执行);

▪ 6.3 坑3:使用Executors创建线程池

// ❌ 错误配置ExecutorService pool = Executors.newFixedThreadPool(10);// 内部用LinkedBlockingQueue(无界队列),任务堆积会OOM

解决方案:

// ✅ 正确配置ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100),  // 有界队列    new ThreadPoolExecutor.CallerRunsPolicy()  // 拒绝策略);

▪ 6.4 坑4:线程池没有命名

// ❌ 错误配置ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100));// 线程名字是”pool-1-thread-1″,无法区分不同线程池

解决方案:

// ✅ 正确配置ThreadFactory threadFactory = new ThreadFactoryBuilder()    .setNameFormat(“task-executor-%d”)  // 线程名字前缀    .build();ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100),    threadFactory);

▪ 6.5 坑5:不监控线程池

// ❌ 错误配置ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100));// 没有监控,不知道线程池的状态和性能

解决方案:

// ✅ 正确配置ThreadPoolExecutor executor = new ThreadPoolExecutor(    5, 10, 60, TimeUnit.SECONDS,    new ArrayBlockingQueue<>(100));// 添加监控@Scheduled(fixedRate = 10000)public void monitor() {    log.info(“活跃线程数: {}”, executor.getActiveCount());    log.info(“队列深度: {}”, executor.getQueue().size());    log.info(“已完成任务数: {}”, executor.getCompletedTaskCount());}

七、总结

▪ 7.1 线程池状态机

状态
说明
是否接收新任务
是否处理队列任务
RUNNING
正在运行
✅ 是
✅ 是
SHUTDOWN
正在关闭
❌ 否
✅ 是
STOP
已经停止
❌ 否
❌ 否
TIDYING
正在整理
❌ 否
❌ 否
TERMINATED
已终止
❌ 否
❌ 否

▪ 7.2 execute()方法流程

▪ 7.3 参数调优策略

指标
监控值
阈值
调优策略
活跃线程数
activeCount
> corePoolSize * 0.8
增加corePoolSize
活跃线程数
activeCount
< corePoolSize * 0.3
减少corePoolSize
队列深度
queueSize
> queueCapacity * 0.8
增加maximumPoolSize
拒绝次数
rejectedCount
> 0
检查参数配置

▪ 7.4 异常处理

异常类型
场景
解决方案
RejectedExecutionException
线程池关闭后提交任务
检查线程池状态
ArithmeticException
任务执行失败
try-catch捕获
未捕获异常
线程终止
设置UncaughtExceptionHandler

本文适用于后端开发、架构师、技术面试准备者。

面试官问你:「线程池的7个核心参数是什么?」这么答就对了