线程池简单讲解

核心目标

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

关键参数

  • corePoolSize (核心线程数):线程池中始终保持的线程数量,即使这些线程处于空闲状态(未执行任务),除非设置了allowCoreThreadTimeOut(允许核心线程超时)参数。

  • maximumPoolSize (最大线程数):线程池允许创建的最大线程数量,是线程池能容纳的线程上限。

  • keepAliveTime + unit (空闲线程存活时间 + 时间单位):非核心线程空闲多久被回收;开启 allowCoreThreadTimeOut(true) 后核心也会回收。

  • workQueue (任务队列):用于在任务执行前暂存任务的队列,该队列仅存放通过execute()方法提交的Runnable类型任务,决定"排队 vs 扩容"的策略。

    • SynchronousQueue:同步队列,不存储,直接移交。

    • LinkedBlockingQueue(默认无界):"先排队,后扩容";风险是无界等待,吞吐高、延迟可变。

    • ArrayBlockingQueue(有界):常用于生产,配合 maximumPoolSize 和拒绝策略。

    • PriorityBlockingQueue:按优先级执行任务。

  • threadFactory (线程工厂):线程池创建新线程时所使用的 "工厂类",用于统一定义线程的名称、优先级、是否为守护线程(daemon thread)等属性。

  • handler (拒绝策略):当线程池的线程数已达到maximumPoolSize(最大线程数),且任务队列(workQueue)也已装满时,新提交的任务会被 "拒绝",此时将由该处理器定义拒绝任务的处理方式。

    • AbortPolicy(默认):直接抛出RejectedExecutionException异常,中断任务提交;
    • CallerRunsPolicy:由提交任务的 "调用线程"(如主线程)自行执行该任务,减缓任务提交速度;
    • DiscardPolicy:默默丢弃新任务,不抛出异常也不处理;
    • DiscardOldestPolicy:丢弃任务队列中 "最旧" 的未处理任务,然后尝试提交新任务。

源码解读

  • AtomicInteger ctl: 主线程池控制状态,高位表示线程池运行状态,低位表示"工作线程数"。
    • runState「高 3 位」:表示线程池状态(如运行中、关闭中等)
    • workerCount「低 29 位」:表示有效线程数
  • class Worker extends AbstractQueuedSynchronizer implements Runnable: 主要维护运行任务的线程的中断控制状态。继承 AQS,以简化每次任务执行时锁的获取与释放。这样做可以防止那些原本用于唤醒等待任务的工作线程的中断,反而错误地中断了正在运行任务的线程。

任务提交:execute(Runnable command)

java 复制代码
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
  			// 如果运行的线程数少于 corePoolSize,就尝试启动一个新线程,并将给定的任务作为它的第一个任务。
  			// 调用 addWorker 会以原子方式检查 runState 和 workerCount,从而避免错误地增加线程(在不该增加时),通过返回 false 来阻止。
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        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);
        }
  			// 如果任务无法入队,那么就尝试增加一个新线程。如果失败了,就可以确定线程池已经关闭或已饱和,因此拒绝该任务。
        else if (!addWorker(command, false))
            reject(command);
    }

新增工作线程:addWorker(Runnable firstTask, boolean core)

java 复制代码
private boolean addWorker(Runnable firstTask, boolean core) {
        // 阶段一:状态/容量快速判断 + CAS 增量(外层 retry + 内层自旋)
  			retry:
        for (int c = ctl.get();;) {
            // Check if queue empty only if necessary.
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP)
                    || firstTask != null
                    || workQueue.isEmpty()))
                return false;

            for (;;) {
              	// 判断工作线程,是否大于核心线程数或者最大线程数
                if (workerCountOf(c)
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;
              	// CAS 增加 workerCount,成功跳出两层循环,失败重新获取状态,失败原因是SHUTDOWN还是计数竞争
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateAtLeast(c, SHUTDOWN))
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
  			// 阶段二:构造 Worker 并在互斥下注册
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
              	// 加锁
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int c = ctl.get();

                  	// 仍然是 Running 状态,或者 STOP,没有新任务(清空队列,补线程)
                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                      	// 防止重复启动
                        if (t.getState() != Thread.State.NEW)
                            throw new IllegalThreadStateException();
                      	// worker 添加,更新最大线程数
                        workers.add(w);
                        workerAdded = true;
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
          	// 如果未启动成功,失败处理
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

线程主循环:runWorker(Worker w)

java 复制代码
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
          	// 可能一开始就带有一个初始任务,此时不需要再去获取第一个任务。否则,只要线程池处于运行状态,就会通过 getTask 获取任务。
            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);
        }
    }
相关推荐
fatfishccc43 分钟前
Spring MVC 全解析:从核心原理到 SSM 整合实战 (附完整源码)
java·spring·ajax·mvc·ssm·过滤器·拦截器interceptor
没有bug.的程序员1 小时前
MyBatis 初识:框架定位与核心原理——SQL 自由掌控的艺术
java·数据库·sql·mybatis
执键行天涯1 小时前
从双重检查锁定的设计意图、锁的作用、第一次检查提升性能的原理三个角度,详细拆解单例模式的逻辑
java·前端·github
程序员江鸟1 小时前
Java面试实战系列【JVM篇】- JVM内存结构与运行时数据区详解(私有区域)
java·jvm·面试
回家路上绕了弯1 小时前
ClickHouse 深度解析:从核心特性到实战应用,解锁 OLAP 领域新势能
数据库·后端
架构师沉默1 小时前
Java 状态机设计:替代 if-else 的优雅架构
java·程序员·架构
java亮小白19972 小时前
Spring Cloud 快速通关之Sentinel
java·spring cloud·sentinel
atwednesday2 小时前
大规模文档预览的架构设计与实现策略
java
xiaok2 小时前
本地用VScode的Live Server监听5500访问页面,ubuntu上不需要在配置5500
后端
雨绸缪2 小时前
ABAP 时间戳
后端