线程池简单讲解

核心目标

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

关键参数

  • 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);
        }
    }
相关推荐
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ2 分钟前
配置springdoc swagger开关
java
Echo flower5 分钟前
Spring Boot WebFlux 实现流式数据传输与断点续传
java·spring boot·后端
没有bug.的程序员11 分钟前
微服务中的数据一致性困局
java·jvm·微服务·架构·wpf·电商
鸽鸽程序猿16 分钟前
【Redis】Java客户端使用Redis
java·redis·github
悦悦子a啊16 分钟前
使用 Java 集合类中的 LinkedList 模拟栈以此判断字符串是否是回文
java·开发语言
Lucky小小吴18 分钟前
java代码审计入门篇——Hello-Java-Sec(完结)
java·开发语言
一个想打拳的程序员19 分钟前
无需复杂配置!用%20docker-webtop%20打造跨设备通用%20Linux%20桌面,加载cpolar远程访问就这么简单
java·人工智能·docker·容器
一起养小猫22 分钟前
LeetCode100天Day2-验证回文串与接雨水
java·leetcode
清晓粼溪26 分钟前
Java登录认证解决方案
java·开发语言
小徐Chao努力27 分钟前
Go语言核心知识点底层原理教程【变量、类型与常量】
开发语言·后端·golang