线程池执行流程

news2025/3/10 14:49:27

源码分析

execute(提交)方法源码:

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);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

addWorker(添加线程):

private boolean addWorker(Runnable firstTask, boolean core) {
        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;
                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;
        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();

                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                        if (t.getState() != Thread.State.NEW)
                            throw new IllegalThreadStateException();
                        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;
    }

addWorker方法

先看addWorker方法的第一部分,这个方法弄懂了,execute方法就非常好理解:

第一部分

在这里插入图片描述
先讲大致意思:

  1. 使用标志位并进入死循环
  2. 判断当前线程池状态、任务是否为空、阻塞队列是否为空
  3. 根据core判断当前线程量(区分核心线程、最大线程数量)是否达到最大限制
  4. 将当前线程数量+1,只有此方法成功才可退出死循环
  5. 判断当前状态,为true跳到标志位处,跳过本次循环,开启下次循环

首先一进来方法,就是两个死循环:

for(int c =ctl.get();;){
	for(;;){
		...
	}
}

而唯一跳出死循环的办法就在第四步:

break retry;

可能很多人不理解为什么break要这么写,这其实就是标志位的写法,很多源码中都能看到
详情参考链接:java retry: 详解

所以break retry的含义就是 跳入到标志位处,且不再进入标志位下方的循环
continue retry:跳入到标志位处,跳过本次循环,进入下次循环

我们再来看看方法块中成员变量ctl方法runstateAtleast()compareAndIncrementWorkerCount()

成员变量ctl

在这里插入图片描述
源码中简单说明了ctl是一个成员变量,主要作用是记录线程池的生命周期状态当前线程数,ctl是原子性的,作者通过巧妙的设计,将一个整形变量按二进制分为两部分,也就是说ctl中的成员变量value通过拆装箱可以表达两种含义(线程池的生命周期状态、当前线程数),想了解更深的可以参考链接(反正我是看不懂):详解Java线程池的ctl(线程池控制状态)【源码分析】

线程池的声明周期状态:

  • RUNNING:处于RUNNING状态的线程池能够接受新任务,以及对新添加的任务进行处理。
  • SHUTDOWN:处于SHUTDOWN状态的线程池不可以接受新任务,但是可以对已添加的任务进行处理。
  • STOP:处于STOP状态的线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
  • TIDYING:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
  • TERMINATED:线程池彻底终止的状态。

拆装箱方法详解:线程池(二、ctl 的设计分析)

runStateAtLeast()

字面意思:线程池状态大于等于xx
来看这个方法使用:

runStateAtLeast(ctl.get(), SHUTDOWN))

  private static boolean runStateAtLeast(int c, int s) {
        return c >= s;
    }

ctl.get():就是获取ctl的成员变量value,之前也说到成员value是可以表达两种含义的(线程池的生命周期状态、当前线程数),这里就有用到表达声明周期状态的用法。

SHUTDOWN是有初始化的,所以方法可以简单理解成:若当前ctl的生命周期状态>=SHUTDOWN就证明当前线程池的生命周期状态是SHUTDOWN状态或者是其它状态,但绝不是RUNNING状态,线程池初始化的五种状态,值越大就代表当前线程池就越危险。
在这里插入图片描述

workerCountOf()

获取当前线程池的已有的线程数量。

compareAndIncrementWorkerCount©

/**
     * Attempts to CAS-increment the workerCount field of ctl.
     */
    private boolean compareAndIncrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect + 1);
    }



	/**
     * Atomically sets the value to {@code newValue}
     * if the current value {@code == expectedValue},
     * with memory effects as specified by {@link VarHandle#compareAndSet}.
     *
     * @param expectedValue the expected value
     * @param newValue the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expectedValue, int newValue) {
        return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
    }

将当前ctl的value值加1,可以理解成当前线程数量+1

第二部分

在这里插入图片描述

  1. 新建线程,不区分核心线程、非核心线程,核心线程不会被删除,其实可以理解成保留核心线程大小的线程(保留maximumPoolSize个线程)
  2. 加锁,保证线程安全进行加锁。
  3. 判断当前线程池时状态,将新建的线程加入线程队列/集合(管理线程),线程队列在线程池创建时就创建,所以可以浅显的理解成线程池其实有两个队列,分别是阻塞队列(workQueue)-管理任务、线程队列(workers)-管理线程
  4. 解锁
  5. 执行任务,注意这里是直接执行任务了,并没有交给阻塞队列
  6. 回退,将刚加入的线程取消

Worker

Woker就是线程池的内部类,构造方法就是调用线程工厂新建线程

 /**
         * 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;
            this.thread = getThreadFactory().newThread(this);
        }


    /**
     * Returns the thread factory used to create new threads.
     *
     * @return the current thread factory
     * @see #setThreadFactory(ThreadFactory)
     */
    public ThreadFactory getThreadFactory() {
    	// 返回当前线程池的线程工厂
        return threadFactory;
    }

execute方法

在这里插入图片描述

  1. 判断当前线程数量是否小于核心线程个数,小于则直接新增线程并直接执行任务(注意是直接,没有放入到阻塞队列中),
  2. 大于则判断当前线程池状态是否为RUNNING,并将任务当前加入阻塞队列中
  3. 加入阻塞队列成功后 需要判断当前线程池状态是否正常,不正常则回退阻塞队列(remove(command))并执行拒绝策略(reject(command))
  4. 加入阻塞队列成功后 需要判断当前线程数量是否为0(当核心线程数为0时,会导致任务无法被线程执行),为true则新增一个线程但并没有执行任务,因为任务已经添加进入阻塞队列了,且firstTask为null,所以addWorker方法执行的任务是空的
  5. 加入阻塞队列失败后 创建一个新线程并执行马上执行任务,若是创建线程失败(可能的原因:线程数量已达到最大限制、线程池状态异常)则采用拒绝策略

这里可能大部分不会理解,为什么在第二步加入阻塞队列之后还需要再判断有没有线程、线程池状态,这里因为在创建线程池的时候,核心线程大小个数可能会被设置成0,若是设置为0,则可以跳过第一步的判断,若是没有第四步则会导致任务都会进入阻塞队列,但是却没有线程执行任务

总结

在这里插入图片描述

  1. 先查看当前线程池的线程数量是否小于核心线程数大小,小于则新建线程并直接执行任务
  2. 大于则放入阻塞队列中,加入阻塞后,任务在未来某个时间点被一个空闲的线程取出执行,但是在加入到阻塞队列后,还会检查当前是否有线程存在(因为当核心线程数量设置为0的时候,程序也会执行到一步,但是会出现一个情况:阻塞队列里的任务无法被提取执行,因为当前线程池并没有线程,所有这里会创建一个线程)
  3. 当阻塞队列也满了,则会新建线程并立即执行任务
  4. 若线程数量也达到最大限制就会执行对应的拒绝策略

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1102117.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python打开.npy文件的常见报错及解决

import numpy as npdata np.load("texture_data_256.npy") print(data) 解决办法&#xff1a; import numpy as npdata np.load("texture_data_256.npy",allow_pickleTrue) print(data) 再次运行后出现乱码&#xff01;&#xff01;&#xff01; 由于…

山西电力市场日前价格预测【2023-10-18】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-18&#xff09;山西电力市场全天平均日前电价为348.72元/MWh。其中&#xff0c;最高日前电价为505.50元/MWh&#xff0c;预计出现在18: 00。最低日前电价为288.10元/MWh&#xff0c;预计…

安科瑞能耗监测系统在新集卫生院综合楼、急诊楼的设计与应用

安科瑞 崔丽洁 摘要&#xff1a;针对医院建筑能耗高且能源管理不合理的问题&#xff0c;利用计算机网络技术、通讯技术、计量控制技术等信息化技术&#xff0c;实现能源资源分类分项计量和能源资源运行监管功能&#xff0c;清晰描述建筑内总的用能现状&#xff1b;实时监测各供…

什么是著作权?对此你了解多少?

在当今信息爆炸的时代&#xff0c;著作权成为一个备受关注的话题。创作是人类文明的重要组成部分&#xff0c;而著作权是创作者对自己作品的劳动和智慧的一种保护。很多人还不太了解著作权&#xff0c;那么希望看完此文&#xff0c;你会对它有一个新的认识。 一、著作权的概念 …

入职后快速配置mac方便快速上手业务for研测向

文章目录 下载基本工具配置 mac 基本修改 mac 密码apple id 登录mac 手势操作 配置开发环境homebrewgitjava/goland/pythonmavenrpcadbmysqlredis前端环境 软件配置配置软件通知chrome 配置配置 jetbrains 插件chrome 插件配置 iterm2配置邮箱视频软件配置软件登录 公司相关配置…

GPIO基本原理

名词解释 高低电平&#xff1a;GPIO引脚电平范围&#xff1a;0V~3.3V&#xff08;部分引脚可容忍5V&#xff09;数据0就是0V&#xff0c;代表低电平&#xff1b;数据1就是3.3V&#xff0c;代表高电平&#xff1b; STM32是32位的单片机&#xff0c;所以内部寄存器也都是32位的…

扫雷游戏源码解析:构建你自己的MineSweeper

大家好&#xff0c;我自己编写了一款扫雷游戏&#xff0c;并决定将其开源。在这个项目中&#xff0c;您可以体验初级、中级和高级难度的游戏模式&#xff0c;适合各种游戏水平。如果您热爱扫雷或对编程有兴趣&#xff0c;这个项目一定会吸引您。 项目亮点&#xff1a; 三种难度…

【数据结构】栈(C语言实现)

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 栈 1.栈1.1栈的概念及结构…

Dynamics 365 QueryExpression生成工具

D365后端的查询构建语句有QueryExpression和FetchExpression&#xff0c;一般在涉及多表查询的时候会用FetchExpression多一些&#xff0c;因为结构看起来比较直观&#xff0c;而且生成也有多种方式&#xff0c;比如高级查找构建后下载XML&#xff0c;比如Xrmtoolbox中的fetch …

Unity 3D 基础——Coroutine 协同程序

Coroutine 称为协同程序或者协程&#xff0c;协同程序可以和主程序并行运行&#xff0c;和多线程有些类似。协同程序可以用来实现让一段程序等待一段时间后继续运行的效果。例如&#xff0c;执行步骤1&#xff0c;等待3秒&#xff1b;执行步骤2&#xff0c;等待某个条件为 true…

微信小程序首页-----布局(详细教程赶快收藏吧)

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 越努力 &#xff0c;越幸运。 1.flex弹性布局 Flex是Flexible Box的缩写&#xff0c;意为”弹性布局”&#xff0c;用来为盒状模型提供最大…

被无视的小细节

最近要写一个web服务,对外提供几个Api,选用gin框架. gin的路由广泛为人好评,一直是"gin为什么这么快"的主角. 正在看其用到的httprouter的源码, 对这种特殊的trie树—基树树(Radix Tree),也有还不错的理解. ( httprouter包只有200多k,非常值得一看 ) 就在自以为洞若观…

linux虚机新增加磁盘后在系统中查不到

问题描述 在虚机管理平台上对某一linux主机添加了一块硬盘&#xff0c;但在系统中并未显示 通过执行 lsblk&#xff0c;并未看到新增的硬盘信息 解决方法 1. 可通过重启服务器解决 2. 如果不能重启服务器&#xff0c;可重新扫描下 scsi总线 查看总线&#xff1a; ls /s…

那些年,我们追过的Go BUG

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

多媒体应用设计师 第9章 信息安全性知识

1.信息安全性基本概念 2.计算机病毒防范 2.1.计算机病毒的分类与识别 病毒特点&#xff1a;隐蔽性&#xff0c;传染性&#xff0c;破坏性&#xff0c;潜伏性 计算机病毒构成:感染标志(病毒签名)&#xff0c;引导模块&#xff0c;感染模块&#xff0c;破坏模块(表现模块) 病…

机器学习算法 —— 1. K近邻算法

K近邻算法 1. K近邻算法简介2. K近邻算法常见距离度量2.1 欧氏距离&#xff08;Euclidean Distance&#xff09;2.2 曼哈顿距离&#xff08;Manhattan Distance &#xff09;2.3 切比雪夫距离&#xff08;Chebyshev Distance&#xff09;2.4 闵可夫斯基距离&#xff08;Minkows…

PCLVisualizer显示点云的深层用法

以下代码均是在QT中使用QVTKOpenGLNativeWidget的简单教程以及案例-CSDN博客文章的基础上&#xff0c;修改按钮对应的槽函数中的程序。 1.显示文件中点云颜色属性信息&#xff0c;利用PointCloudColorHandlerRGBField得到每个点云对应的颜色。 pcl::PointCloud<pcl::PointX…

vue3学习(十)--- 依赖注入Provide 和 Inject

文章目录 Provide 和 Inject兄弟组件通信Event BusMitt库 Provide 和 Inject provide 可以在祖先组件中指定我们想要提供给后代组件----子、孙等组件的数据或方法&#xff0c;而在任何后代组件中&#xff0c;我们都可以使用 inject 来接收 provide 提供的数据或方法。 父组件…

yield方法的使用

yield的作用就是主动释放CPU的执行权,会将线程从运行状态转为就绪状态,让后调度执行其他线程 使用方法如下: public class YieldTest {public static void main(String[] args) {ThreadTest t1 new ThreadTest("张三");ThreadTest t2 new ThreadTest("李四&q…

设计师首选:最佳的5款网页设计软件

对于UI设计师来说&#xff0c;网页设计是一项必要的技能。如何做好网页设计&#xff1f;除了设计理念&#xff0c;网页设计和制作软件的应用也是不可或缺的。目前市场上有很多这样的软件&#xff0c;工人要想做好&#xff0c;就必须先磨利工具。如果他们想做网页设计&#xff0…