源码分析--ThreadPoolExecutor(二)

Shopee滑块验证码太刁钻?手把手教你用Python+Playwright绕过弧形验证(附影刀RPA接入代码) 本文详细介绍了如何使用Python和Playwright结合SadCaptcha第三方服务高效破解Shopee平台的弧形滑块验证码,并集成到影刀RPA工作流中。该方案开发周期短、准确率高(>90%),特别适合跨境电商自动化操作,已在实际项目中验证成功。 阅读详情

常用方法

核心方法:execute

// command 是一个 Runnable 对象,也就是用户提交执行的任务
public void execute(Runnable command) {
    // 提交的任务为空时抛出异常
    if (command == null)
        throw new NullPointerException();

    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    // 获取当前 ctl (存有线程池状态和线程数量)
    int c = ctl.get();

    // 若当前工作线程数量小于核心池大小(coolPoolSize)
    // 则在核心池中新增一个工作线程,并将该任务交给这个线程执行
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        // 重新获取(存在并发可能)
        c = ctl.get();
    }

    // 若执行到这里,表示池中线程数量 >= corePoolSize,或者上面 addWorker 失败
    // 若线程池处于 RUNNING 状态,并且该任务(command)成功添加到任务队列
    if (isRunning(c) && workQueue.offer(command)) {
        // 再次获取 ctl 值
        int recheck = ctl.get();
        // 若线程池不是运行状态,则要把上面添加的任务从队列中移除并执行拒绝策略
        //(可理解为“回滚”操作)
        if (! isRunning(recheck) && remove(command))
            // 执行拒绝策略           
            reject(command);
        // 若此时池中没有线程,则新建一个
        // PS: 这里是防止任务提交后,池中没有存活的线程了
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }

    // 根据上述代码分析,若执行到这里,可分为以下两种情况:
    // ① 线程池不是 RUNNING 状态;
    // ② 线程池处于 RUNNING 状态,且实际线程数量 workCount >= corePoolSize,
    //   并且,添加到 workQueue 失败(已满)
    // 此时,则需要和 maximumPoolSize 进行比较,
    //   若 workCount <= maximumPoolSize, 则新建一个线程去执行该任务;
    //   否则,即 workCount > maximumPoolSize (饱和),则执行拒绝策略
    else if (!addWorker(command, false))
        // 执行拒绝策略
        reject(command);
}

该方法描述的就是一个任务提交到线程池的流程,主要执行逻辑如下:

1、若正在运行的线程数少于 corePoolSize,则创建一个新的线程,并将传入的任务(command)作为它的第一个任务执行。
2、若运行的线程数不小于 corePoolSize,则将新来的任务添加到任务队列(workQueue)。若入队成功,仍需再次检查是否需要增加一个线程(上次检查之后现有的线程可能死了,或者进入该方法时线程池 SHUTDOWN 了,此时需要执行回滚);若池中没有线程则新建一个(确保 SHUTDOWN 状态也能执行队列中的任务)。
3、若任务不能入队(队列已满),则创建新的线程并执行任务,若失败(超过 maximumPoolSize),则表示线程池关闭或者已经饱和,因此拒绝该任务。

在线程池中,ctl 贯穿在线程池的整个生命周期中,它是一个原子类,主要作用是用来保存线程数量和线程池的状态。

内部类Worker及其方法:

// 继承自 AQS,且实现了 Runnable 接口
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread;
    /** Initial task to run.  Possibly null. */
    // 运行的第一个任务,可能为空
    Runnable firstTask;
    /** Per-thread task counter */
    volatile long completedTasks;
    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        // 初始化 thread
        this.thread = getThreadFactory().newThread(this);
    }
    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this);
    }
    // 其他一些 AQS 相关的方法不再一一列举
}

addWorker 方法:
在execute方法里可以看到这个方法,如果工作线程数小于核心线程数的话,会调用 addWorker,顾名思义,其实就是要创建一个工作线程。

// firstTask: 第一个任务,可为空
// core: 是否为核心池,true 是,false 为最大池
private boolean addWorker(Runnable firstTask, boolean core) {
    // 该循环的主要作用就是增加 workCount 计数,增加成功后再新增 Worker 对象
    retry://goto 语句,避免死循环
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        /*
         * rs >= SHUTDOWN 表示线程池不再接受新的任务
         * 该判断条件分为以下三种:
         *   ① 线程池处于 STOP, TYDING 或 TERMINATED 状态;
         *   ② 线程池处于 SHUTDOWN 状态,且 firstTask 不为空;
         *   ③ 线程池处于 SHUTDOWN 状态,且 workQueue 为空
         * 满足任一条件即返回 false.
         */
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {//自旋
            int wc = workerCountOf(c);
            // 超出最大容量 CAPACITY,或者超出初始设置的核心池/最大池数量,则返回 false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // CAS 方式增加 ctl 的 workerCount 数量(该循环的主要目的)
            if (compareAndIncrementWorkerCount(c))
                break retry; // 若增加失败则退出循环
            c = ctl.get();  // Re-read ctl
            // 运行状态改变
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
    // 标记 Worker 是否启动、是否添加成功
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 将 firstTask 封装成 Worker 对象
        w = new Worker(firstTask);
        // 获取 thread 对象 t
        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());
                // 若线程池状态小于 SHUTDOWN,即为 RUNNING 状态;
                // 或者为 SHUTDOWN 状态,且 firstTask 为空,
                //   表示不再接受新的任务,但会继续执行队列中的任务
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // 添加到工作线程集合(HashSet)
                    workers.add(w);
                    // 更新最大计数
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    // 标记 Worker 添加成功
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 若成功添加到工作线程集合,则启动线程执行任务
            if (workerAdded) {
                // 启动线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // Worker 启动失败,执行回滚操作
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

addWorker 方法中,如果添加 Worker 并且启动线程失败,则会做失败后的处理。

    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

runWorker 方法:
这个方法主要做几件事

1、如果 task 不为空,则开始执行 task
2、如果 task 为空,则通过 getTask()再去取任务,并赋值给 task,如果取到的 Runnable 不为空,则执行该任务
3、执行完毕后,通过 while 循环继续 getTask()取任务
4、如果 getTask()取到的任务依然是空,那么整个 runWorker()方法执行完毕

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        //unlock,表示当前 worker 线程允许中断,因为 new Worker 默认的 state=-1,此处是调用
        //Worker 类的 tryRelease()方法,将 state 置为 0,
        //而 interruptIfStarted()中只有 state>=0 才允许调用中断
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //注意这个 while 循环,在这里实现了 [线程复用] // 如果 task 为空,则通过getTask 来获取任务
            while (task != null || (task = getTask()) != null) {
                w.lock(); //上锁,不是为了防止并发执行任务,为了在 shutdown()时不终止正在运行的 worker
                //线程池为 stop 状态时不接受新任务,不执行已经加入任务队列的任务,还中断正在执行的任务
                //所以对于 stop 状态以上是要中断线程的
                //(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP)确保线程中断标志位为 true 且是 stop 状态以上,接着清除了中断标志
                //!wt.isInterrupted()则再一次检查保证线程需要设置中断标志位
                if ((runStateAtLeast(ctl.get(), STOP) ||
                        (Thread.interrupted() &&
                                runStateAtLeast(ctl.get(), STOP))) &&
                        !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);//这里默认是没有实现的,在一些特定的场景中我们可以自己继承 ThreadpoolExecutor 自己重写
                    Throwable thrown = null;
                    try {
                        task.run(); //执行任务中的 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 {
                        afterExecute(task, thrown); //这里默认默认而也是没有实现
                    }
                } finally {
                    //置空任务(这样下次循环开始时,task 依然为 null,需要再通过 getTask()取) + 记录该 Worker 完成任务数量 + 解锁
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
            //1.将入参 worker 从数组 workers 里删除掉;
            //2.根据布尔值 allowCoreThreadTimeOut 来决定是否补充新的 Worker 进数组workers
        }
    }
YOLOv11的扩散模型去噪预处理-(在推理前对低质量图像进行增强) 摘要: 扩散模型与YOLOv11的结合有效提升了复杂场景下的目标检测性能。针对低质量图像(如噪声、低光照)导致YOLOv11特征提取退化的问题,研究提出了一种基于条件扩散模型的预处理方法。通过语义引导的渐进式去噪,在保留关键检测特征的同时,显著改善了图像质量。技术核心包括:1)利用YOLOv11浅层特征作为条件输入;2)交叉注意力机制实现任务感知去噪;3)知识蒸馏与潜空间扩散加速推理。实验表明,该方法使mAP50提升19%,在15步内达到传统百步去噪效果,平衡了精度与实时性需求,为鲁棒性目标检测提供了新思路 阅读详情

相关推荐

Stata实战:5步搞定PSM倾向得分匹配(附完整代码与常见报错解决)

本文详细介绍了使用Stata进行PSM(倾向得分匹配)的5步实战流程,包括数据预处理、模型构建、匹配算法选择、匹配质量诊断和结果解释。提供完整代码示例和常见报错解决方案,帮助研究者高效完成实证分析,提升匹配成功率。

weixin_29214559的博客 590

线程池ThreadPoolExecutor详解(整理详细)

线程池ThreadPoolExecutor详解(整理详细)

yuechuzhixing的博客 3万+

华为 S2600T 存储 3 种网络端口(H4/H5/串口)用途解析与故障恢复实战

本文深度解析华为S2600T存储设备的三种关键网络端口(H4/H5/串口)的功能用途与故障恢复方法。通过详细的配置指南和实战案例,帮助管理员掌握管理接口(H4)的日常运维、维护接口(H5)的高级应用以及串口的应急操作,提升存储设备的可靠性和维护效率。

weixin_30899023的博客 336

ThreadPoolExecutor详解

一、ThreadPoolExecutor类讲解 1、线程池状态: 五种状态: 线程池的状态 说明 RUNNING 允许提交并处理任务 SHUTDOWN 不允许提交新的任务,但是会处理完已提交的任务 STOP 不允许提交新的任务,也不会处理阻塞队列中未执行的任务, 并设置正在执行的线程的中断标志位 TIDYING .

赶路人儿 11万+

量化投资进阶:Fama-French三因子模型实战解析与Python实现

本文深入解析了Fama-French三因子模型,并提供了完整的Python实战指南。文章阐述了该模型如何通过引入市值因子(SMB)和价值因子(HML),对传统CAPM模型进行重要扩展,以更精准地解释股票收益来源。详细演示了使用Python获取数据、执行回归分析并解读结果(包括关键的阿尔法α)的全过程,并探讨了该模型在量化策略构建、风格诊断和业绩归因中的核心应用。

yog99的博客 1126

7.12真题华为OD机试真题 新系统 - 字符串压缩编码 (Java/Py/C/C++/Js/Go)

7.12真题华为OD机试真题 新系统 - 字符串压缩编码 (Java/Py/C/C++/Js/Go)

算法大师! 834

【转】线程池ThreadPoolExecutor(二)

 作为Executor框架中最核心的类,ThreadPoolExecutor代表着鼎鼎大名的线程池,它给了我们足够的理由来弄清楚它。内部状态  线程有五种状态:新建,就绪,运行,阻塞,死亡;  线程池同样有五种状态:Running, SHUTDOWN, STOP, TIDYING, TERMINATED。1234567891011121314151617public class ThreadPoo...

男儿当自强 496

GEE生物量碳储量——利用sens和MK检验方法计算1987-2022年森林地上生物量AGB和碳储量的时空变化特征

本文是将之前已经处理好的森林生物量和碳储量数据保存到GEE Assets中,然后分别将单张影像导入到代码编辑器中,构建一个时间序列集合,并且这里需要用到的是我们给影像添加指定的时间属性,这样方便进行下一步的时序分析和空间预测。首先,需要收集1987年至2022年期间森林地上生物量AGB和碳储量数据。该数据可以通过森林清查数据、卫星遥感数据等途径获取。然后,根据这些数据,可以使用sens(敏感性分析)和MK(Mann-Kendall)方法计算时空变化特征。

此星光明博客 1264

JDK源码分析-ThreadPoolExecutor

概述ThreadPoolExecutor 是 JDK 中线程池的实现类,它的继承结构如下:本文主要分析ThreadPoolExecutor类的主要方法和实现原理(部分...

WriteOnRead 270

AOSP 8.0.0编译中的ANDROID_DATA缺失问题解决方案

在AOSP编译过程中,环境变量和路径设置的正确性至关重要。遇到相关的错误时,检查并修正环境配置通常是解决问题的关键。希望本文提供的分析和解决方案能帮助你顺利编译AOSP。

m0_74337424的博客 397

高速信号设计中的关键测试项目与测试仪器详解

高速信号设计是一个多维度协同优化的系统工程。时域分析如同外科手术刀般精准定位物理缺陷,频域分析提供传输通道的频谱指纹,信号质量评估量化系统鲁棒性,接收端容限测试验证极端工况下的可靠性,协议一致性确保生态互操作性,而电源完整性则直指诸多信号问题的根源。

u014344410的博客 217

ThreadPoolExecutor源码分析二

接上文,这里继续分析源码 private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1; // runState is stored in the high-order bit...

dlmk73708的博客 94

手机VPN的连接方法分享

6、返回到VPN页面,可以看到刚刚新添加的VPN,点击开启VPN后面的开关。5、在打开的添加VPN的页面中输入VPN的信息后点击右上角的“保存”按钮。7、待VPN开关开启后,可以看到添加的VPN正在连接的提示信息了。2、在打开的小米手机设置页面中点击“更多连接方式”的菜单项。3、然后在打开的更多连接方式页面中点击VPN的菜单项。4、在打开的VPN设置页面中点击“添加VPN”的按钮。1、首先在手机屏幕左右的滑动,找到手机的设置图标。

惠惠软件 3万+

线程池ThreadPoolExecutor源码分析二(ReentrantLock)

可重入锁,锁如其名,可以重复进入:当一个线程事先拥有了锁之后,后续该线程还可以再一次拥有该锁。 1.1Syn内部抽象类

小小史莱姆的博客 854

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

线程池ThreadPoolExecutor源码分析(二)

需要注意的是核心线程正常情况下,不会跳出while循环(会在getTask方法中阻塞)不会去执行processWorkerExit方法。简单来说getTask方法就是以阻塞的方式从阻塞队列中拿对象,要么一直阻塞要么定时阻塞,定时阻塞没拿到就返回null,后续方法将销毁线程。实现了AQS,用来控制线程是否可中断,实现了Runnable接口,Thread对象创建传的就是Worker对象。该方法会构建一个worker对象,并于此执行任务。接上一篇博文,现在开始分析。

qq_41184048的博客 186

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

并发编程——ThreadPoolExecutor源码分析(二)

前言 在上一篇中,我们分析了ThreadPoolExecutor中关键变量ctl,这篇我们继续来看ThreadPoolExecutor中的构造函数及其参数。其中参数的相关解释来源于源码中的相关注释。 构造函数 我们可以看到ThreadPoolExecutor有四个构造函数: 他们其实都是调用其中的全参数的构造函数,只不过有一些参数是使用了默认提供的参数。我们可以看一下构造函数: public ...

zlj1217的博客 436

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

ThreadPoolExecutor源码分析(二):任务提交主逻辑execute()

作为一个执行服务ExecutorService的实例,ThreadPoolExecutor的首要任务当然是提交任务进行处理,那么,在任务提交时,ThreadPoolExecutor的处理流程是怎样的?它是一味的开启线程进行处理,还是有一套完整的逻辑来处理呢?本文,我们将继续分析ThreadPoolExecutor,看下它在任务提交时的主逻辑。

辰辰爸的博客 3753

并发编程(七)-ThreadPoolExecutor源码分析(二)

如果允许核心线程超时回收,或者当前线程池中正在运行的 Worker 线程数大于核心线程数,则将 timed 设为 true,表示当前线程池中的 Worker 线程需要被回收。如果线程池的运行状态为SHUTDOWN以下,或者运行状态为SHUTDOWN且要添加的任务为null,则将新的工作线程添加到工作线程列表workers中,并更新最大线程池大小largestPoolSize。获取Worker对象中的锁,如果线程池处于STOP状态或者线程被中断并且线程池处于STOP状态或者线程被中断,则中断该线程。

流星1688 569

线程池ThreadPoolExecutor源码分析(二)--ThreadPoolExecutor添加任务工作单元addWorker()

addWorker()主要分两个部分: 第一部分双重循环的目的是判断线程池的状态,若是当前线程池可以创建线程则通过 CAS 操作增加线程数; 第二部分主要是把并发安全的任务加到 workers 里面,并且启动任务执行 private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); int rs .

zxz9325的博客 424

Java 进阶——多线程优化之线程池 ThreadPoolExecutor源码分析(二)

引言 多线程我想无论是后端开发,还是对于App开发者来说都不会陌生,何况Android强制要求不能在主线程中做网络请求,于是乎,在很多初学者或者App的源码中会出现会多的new Thread…的方式,这样的代码是不优雅而且存在很多的隐患,假如说在用户退出App之后,如果线程内的工作还未执行完毕此时是无法被回收的,更不必说如果是无限循环的线程,那么可能永远无法回收,永远占着内存和CPU资源,这是多...

最美不过,心中有梦,身旁有你! 1025

ThreadPoolExecutor 源码分析(二)--- 队列的应用

一、序言        上一篇,介绍了线程池的基本工作原理,这里会介绍一些里面的一些饱和策略和几个常用的线程池方法的实现原理。   二、源码分析        线程池极力推荐我们用Executors 提供车的各种工厂,来创建我们的线程池,提供了我们常用的几种创建线程池的方式:       newCachedThreadPool():无界线程池,可以自己进行回收。 newFixedT...

Hello World 149

ThreadPoolExecutor 源码分析

以上我们主要分析了 ThreadPoolExecutor 的设计,同时分析了核心方法 execute 的源码。实际 ThreadPoolExecutor 也可以支持 submit,即提交任务,与 execute 不同的时,execute 提交后任务执行,没有返回值,而 submit 则可以返回一个 Future,并在后续可以从 Future 中获得任务的执行结果。

u010922759的博客 1639

java线程池ThreadPoolExecutor原理源码分析

java线程池实现原理,线程池远吗分析

qq_34735262的博客 1848

ThreadPoolExecutor类源码分析

Java中的线程池通过复用线程来提高多线程任务的执行效率,而ThreadPoolExecutor就是Java中线程池的核心类之一,它将线程的创建、管理以及资源调度进行了封装,使用ThreadPoolExecutor能够方便地管理多个线程的生命周期和执行。

一名Java语言狂热分子。 923
上一篇: 源码分析--ThreadPoolExecutor(一)
下一篇: 源码分析--ThreadLocal(一)
CodersCoder
博客等级 码龄10年 90粉丝 258原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值