Java 源码 - ThreadPoolExecutor 源码解析_threadpoolexecutor 的源码解析

本文详细分析了JavaThreadPoolExecutor的重要属性,如workerCount、runState等,探讨了线程池状态转换机制,以及任务提交、Worker的执行流程和FutureTask的运用。
摘要由CSDN通过智能技术生成

1.3、ThreadPoolExecutor 重要属性

接下来我们来看一看 ThreadPoolExecutor 都有哪些重要属性,如下:

//ctl 线程池状态控制字段,由两部分组成:
//1:workerCount  wc 工作线程数,我们限制 workerCount 最大到(2^29)-1,大概 5 亿个线程
//2:runState rs 线程池的状态,提供了生命周期的控制,源码中有很多关于状态的校验,状态枚举如下:
//RUNNING(-536870912):接受新任务或者处理队列里的任务。
//SHUTDOWN(0):不接受新任务,但仍在处理已经在队列里面的任务。
//STOP(-536870912):不接受新任务,也不处理队列中的任务,对正在执行的任务进行中断。
//TIDYING(1073741824): 所以任务都被中断,workerCount 是 0,整理状态
//TERMINATED(1610612736): terminated() 已经完成的时候
 
//runState 之间的转变过程:
//RUNNING -> SHUTDOWN:调用 shudown(),finalize()
//(RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()
//SHUTDOWN -> TIDYING -> workerCount ==0
//STOP -> TIDYING -> workerCount ==0
//TIDYING -> TERMINATED -> terminated() 执行完成之后
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;// 29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;// =(2^29)-1=536870911
 
// Packing and unpacking ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int runStateOf(int c)     { return c & ~CAPACITY; }
 
// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;//-536870912
private static final int SHUTDOWN   =  0 << COUNT_BITS;//0
private static final int STOP       =  1 << COUNT_BITS;//-536870912
private static final int TIDYING    =  2 << COUNT_BITS;//1073741824
private static final int TERMINATED =  3 << COUNT_BITS;//1610612736
 
// 已完成任务的计数
volatile long completedTasks;
// 线程池最大容量
private int largestPoolSize;
// 已经完成的任务数
private long completedTaskCount;
// 用户可控制的参数都是 volatile 修饰的
// 可以使用 threadFactory 创建 thread
// 创建失败一般不抛出异常,只有在 OutOfMemoryError 时候才会
private volatile ThreadFactory threadFactory;
// 饱和或者运行中拒绝任务的 handler 处理类
private volatile RejectedExecutionHandler handler;
// 线程存活时间设置
private volatile long keepAliveTime;
// 设置 true 的话,核心线程空闲 keepAliveTime 时间后,也会被回收
private volatile boolean allowCoreThreadTimeOut;
// coreSize
private volatile int corePoolSize;
// maxSize 最大限制 (2^29)-1
private volatile int maximumPoolSize;
// 默认的拒绝策略
private static final RejectedExecutionHandler defaultHandler =
    new AbortPolicy();
 
// 队列会 hold 住任务,并且利用队列的阻塞的特性,来保持线程的存活周期
private final BlockingQueue<Runnable> workQueue;
 
// 大多数情况下是控制对 workers 的访问权限
private final ReentrantLock mainLock = new ReentrantLock();
private final Condition termination = mainLock.newCondition();
 
// 包含线程池中所有的工作线程
private final HashSet<Worker> workers = new HashSet<Worker>();

属性也是非常多,为了方便理解线程池的状态扭转,画了一个图:

图片描述

Worker 我们可以理解成线程池中任务运行的最小单元,Worker 的大致结构如下:

// 线程池中任务执行的最小单元
// Worker 继承 AQS,具有锁功能
// Worker 实现 Runnable,本身是一个可执行的任务
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    // 任务运行的线程
    final Thread thread;
 
    // 需要执行的任务
    Runnable firstTask;
 
    // 非常巧妙的设计,Worker本身是个 Runnable,把自己作为任务传递给 thread
    // 内部有个属性又设置了 Runnable
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        // 把 Worker 自己作为 thread 运行的任务
        this.thread = getThreadFactory().newThread(this);
    }
 
   /** Worker 本身是 Runnable,run 方法是 Worker 执行的入口, runWorker 是外部的方法 */
    public void run() {
        runWorker(this);
    }
 
    private static final long serialVersionUID = 6138294804551838833L;
 
    // Lock methods
    // 0 代表没有锁住,1 代表锁住
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }
    // 尝试加锁,CAS 赋值为 1,表示锁住
    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
    // 尝试释放锁,释放锁没有 CAS 校验,可以任意的释放锁
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
 
    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }
 
    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

理解 Worker 非常关键,主要有以下几点:

  1. Worker 很像是任务的代理,在线程池中,最小的执行单位就是 Worker,所以 Worker 实现了 Runnable 接口,实现了 run 方法;
  2. 在 Worker 初始化时 this.thread = getThreadFactory ().newThread (this) 这行代码比较关键,它把当前 Worker 作为线程的构造器入参,我们在后续的实现中会发现这样的代码:Thread t = w.thread;t.start (),此时的 w 是 Worker 的引用申明,此处 t.start 实际上执行的就是 Worker 的 run 方法;
  3. Worker 本身也实现了 AQS,所以其本身也是一个锁,其在执行任务的时候,会锁住自己,任务执行完成之后,会释放自己。

2、线程池的任务提交

线程池的任务提交从 submit 方法说起,submit 方法是 AbstractExecutorService 抽象类定义的,主要做了两件事情:

  1. 把 Runnable 和 Callable 都转化成 FutureTask,这个我们之前看过源码了;
  2. 使用 execute 方法执行 FutureTask。

execute 方法是 ThreadPoolExecutor 中的方法,源码如下:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    // 工作的线程小于核心线程数,创建新的线程,成功返回,失败不抛异常
    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);
        // 发现可运行的线程数是 0,就初始化一个线程,这里是个极限情况,入队的时候,突然发现
        // 可用线程都被回收了
        else if (workerCountOf(recheck) == 0)
            // Runnable是空的,不会影响新增线程,但是线程在 start 的时候不会运行
            // Thread.run() 里面有判断
            addWorker(null, false);
    }
    // 队列满了,开启线程到 maxSize,如果失败直接拒绝,
    else if (!addWorker(command, false))
        reject(command);
}

execute 方法执行的就是整体架构图的左半边的逻辑,其中多次调用 addWorker 方法,addWorker 方法的作用是新建一个 Worker,我们一起来看下源码:

// 结合线程池的情况看是否可以添加新的 worker
// firstTask 不为空可以直接执行,为空执行不了,Thread.run()方法有判断,Runnable为空不执行
// core 为 true 表示线程最大新增个数是 coresize,false 表示最大新增个数是 maxsize
// 返回 true 代表成功,false 失败
// break retry 跳到retry处,且不再进入循环
// continue retry 跳到retry处,且再次进入循环
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    // 先是各种状态的校验
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // Check if queue empty only if necessary.
        // rs >= SHUTDOWN 说明线程池状态不正常
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;
 
        for (;;) {
            int wc = workerCountOf(c);
            // 工作中的线程数大于等于容量,或者大于等于 coreSize or maxSize
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))
                // break 结束 retry 的 for 循环
                break retry;
            c = ctl.get();  // Re-read ctl
            // 线程池状态被更改
            if (runStateOf(c) != rs)
                // 跳转到retry位置
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
 
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 巧妙的设计,Worker 本身是个 Runnable.
        // 在初始化的过程中,会把 worker 丢给 thread 去初始化
        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 rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 启动线程,实际上去执行 Worker.run 方法
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

addWorker 方法首先是执行了一堆校验,然后使用 new Worker (firstTask) 新建了 Worker,最后使用 t.start () 执行 Worker,上文我们说了 Worker 在初始化时的关键代码:this.thread = getThreadFactory ().newThread (this),Worker(this) 是作为新建线程的构造器入参的,所以 t.start () 会执行到 Worker 的 run 方法上,源码如下:

public void run() {
    runWorker(this);
}

runWorker 方法是非常重要的方法,我们一起看下源码实现:

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    //帮助gc回收
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // task 为空的情况:
        // 1:任务入队列了,极限情况下,发现没有运行的线程,于是新增一个线程;
        // 2:线程执行完任务执行,再次回到 while 循环。
        // 如果 task 为空,会使用 getTask 方法阻塞从队列中拿数据,如果拿不到数据,会阻塞住
        while (task != null || (task = getTask()) != null) {
            //锁住 worker
            w.lock();
            // 线程池 stop 中,但是线程没有到达中断状态,帮助线程中断
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                //执行 before 钩子函数
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    //同步执行任务
                    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 {
                    //执行 after 钩子函数,如果这里抛出异常,会覆盖 catch 的异常
                    //所以这里异常最好不要抛出来
                    afterExecute(task, thrown);
                }
            } finally {
                //任务执行完成,计算解锁
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        //做一些抛出异常的善后工作
        processWorkerExit(w, completedAbruptly);
    }
}

这个方法执行的逻辑是架构图中的标红部分:

图片描述

我们聚焦一下这行代码:task.run () 此时的 task 是什么呢?此时的 task 是 FutureTask 类,所以我们继续追索到 FutureTask 类的 run 方法的源码,如下:

/**
 * run 方法可以直接被调用
 * 也可以由线程池进行调用
 */
public void run() {
    // 状态不是任务创建,或者当前任务已经有线程在执行了
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;


### 最后

小编在这里分享些我自己平时的学习资料,由于篇幅限制,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

**[开源分享:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)**

**程序员代码面试指南 IT名企算法与数据结构题目最优解**

这是” 本程序员面试宝典!书中对IT名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现。针对当前程序员面试缺乏权威题目汇总这一-痛点, 本书选取将近200道真实出现过的经典代码面试题,帮助广“大程序员的面试准备做到万无一失。 “刷”完本书后,你就是“题王”!

![image.png](https://img-blog.csdnimg.cn/img_convert/911cf1d455fbae5e8777d4eac8d97378.webp?x-oss-process=image/format,png)

**《TCP-IP协议组(第4版)》**

本书是介绍TCP/IP协议族的经典图书的最新版本。本书自第1版出版以来,就广受读者欢迎。

本书最新版进行」护元,以体境计算机网络技不的最新发展,全书古有七大部分共30草和7个附录:第一部分介绍一些基本概念和基础底层技术:第二部分介绍网络层协议:第三部分介绍运输层协议;第四部分介绍应用层协议:第五部分介绍下一代协议,即IPv6协议:第六部分介绍网络安全问题:第七部分给出了7个附录。

![image.png](https://img-blog.csdnimg.cn/img_convert/9f32c3bf8ca23a8cb9876485561ae1e4.webp?x-oss-process=image/format,png)


**Java开发手册(嵩山版)**

这个不用多说了,阿里的开发手册,每次更新我都会看,这是8月初最新更新的**(嵩山版)**

![image.png](https://img-blog.csdnimg.cn/img_convert/240c49bcc091d0014cf89ab791e42898.webp?x-oss-process=image/format,png)

**MySQL 8从入门到精通**

本书主要内容包括MySQL的安装与配置、数据库的创建、数据表的创建、数据类型和运算符、MySQL 函数、查询数据、数据表的操作(插入、更新与删除数据)、索引、存储过程和函数、视图、触发器、用户管理、数据备份与还原、MySQL 日志、性能优化、MySQL Repl ication、MySQL Workbench、 MySQL Utilities、 MySQL Proxy、PHP操作MySQL数据库和PDO数据库抽象类库等。最后通过3个综合案例的数据库设计,进步讲述 MySQL在实际工作中的应用。

![image.png](https://img-blog.csdnimg.cn/img_convert/12915a7dc3028f0e78f96e86336bf7e4.webp?x-oss-process=image/format,png)


**Spring5高级编程(第5版)**

本书涵盖Spring 5的所有内容,如果想要充分利用这一领先的企业级 Java应用程序开发框架的强大功能,本书是最全面的Spring参考和实用指南。

本书第5版涵盖核心的Spring及其与其他领先的Java技术(比如Hibemate JPA 2.Tls、Thymeleaf和WebSocket)的集成。本书的重点是介绍如何使用Java配置类、lambda 表达式、Spring Boot以及反应式编程。同时,将与企业级应用程序开发人员分享一些见解和实际经验,包括远程处理、事务、Web 和表示层,等等。

![image.png](https://img-blog.csdnimg.cn/img_convert/0ff0c4b5d0443e598496082486a3431f.webp?x-oss-process=image/format,png)


**JAVA核心知识点+1000道 互联网Java工程师面试题**

![image.png](https://img-blog.csdnimg.cn/img_convert/144272c5d17a9b89d2595792e5d0bf33.webp?x-oss-process=image/format,png)


![image.png](https://img-blog.csdnimg.cn/img_convert/6fbcfda44ead2343f0d9a1b45c80e530.webp?x-oss-process=image/format,png)


**企业IT架构转型之道 阿里巴巴中台战略思想与架构实战**

本书讲述了阿里巴巴的技术发展史,同时也是-部互联网技 术架构的实践与发展史。

![image.png](https://img-blog.csdnimg.cn/img_convert/11a7eb1794cfa90f579d68cf76ee63df.webp?x-oss-process=image/format,png)

比如Hibemate JPA 2.Tls、Thymeleaf和WebSocket)的集成。本书的重点是介绍如何使用Java配置类、lambda 表达式、Spring Boot以及反应式编程。同时,将与企业级应用程序开发人员分享一些见解和实际经验,包括远程处理、事务、Web 和表示层,等等。

[外链图片转存中...(img-WvfQdvx0-1714125184711)]


**JAVA核心知识点+1000道 互联网Java工程师面试题**

[外链图片转存中...(img-QOAEIpO8-1714125184712)]


[外链图片转存中...(img-B4V7DN4a-1714125184712)]


**企业IT架构转型之道 阿里巴巴中台战略思想与架构实战**

本书讲述了阿里巴巴的技术发展史,同时也是-部互联网技 术架构的实践与发展史。

[外链图片转存中...(img-N0FRK7tG-1714125184713)]

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**
  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值