ThreadPoolExecutor源码解析

2023-11-17

ThreadPoolExecutor源码解析

       

    一、新建线程池的是构造方法

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }


各参数解析
corePoolSize – 核心线程数 要保留在池中的线​​程数,即使它们处于空闲状态,除非设置了 allowCoreThreadTimeOut


     当核心线程数被设置为true后,调用interruptIdleWorkers中断空闲线程的方法、
    public void allowCoreThreadTimeOut(boolean value) {
        if (value && keepAliveTime <= 0)
            throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
        if (value != allowCoreThreadTimeOut) {
            allowCoreThreadTimeOut = value;
            if (value)
                interruptIdleWorkers();
        }
    }


    1、 获取当前锁对象并锁住该方法
    2、遍历循环工作线程 判断每个线程如果没有中断,并获取当前线程尝试获取锁成功,代表该线程是空闲线程 那么中断这个线程。
    3、当只有一个线程的时候,不做处理 线程池中始终会保持一个线程
 private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }


maximumPoolSize – 最大线程数 池中允许的最大线程数
keepAliveTime – 当线程数大于核心数时,这是多余空闲线程在终止前等待新任务的最长时间。如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
unit – keepAliveTime 参数的时间单位
workQueue – 底层使用BlockingQueue阻塞队列 用于在执行任务之前保存任务的队列。这个队列将只保存 execute 方法提交的 Runnable 任务。

ThreadFactory:创建线程工厂,可以自定义线程工厂给线程池里的线程设置一个自定义线程名。
     DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

handler:饱和策略,假如线程池已满,并且没有空闲的线程,这个时候不再允许提交任务到线程池,线程池提供了4中策略,至于具体采用哪种策略还是自定义策略,具体情况具体分析。
AbortPolicy:拒绝提交,直接抛出异常,也是默认的饱和策略;
CallerRunsPolicy:线程池还未关闭时,用调用者的线程执行任务;
DiscardPolicy:丢掉提交任务;
DiscardOldestPolicy:线程池还未关闭时,丢掉阻塞队列最久为处理的任务,并且执行当前任务。

如果以下条件之一成立:corePoolSize < 0 keepAliveTime < 0 maximumPoolSize <= 0 maximumPoolSize < corePoolSize

抛出:
IllegalArgumentException 

 

二、看execute方法

 

如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;

如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;

如果队列已经满了,则在总线程数不大于maximumPoolSize的前提下,则创建新的线程

如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;

如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
   1.主池控制状态ctl是一个原子整数,
 封装了两个概念字段workerCount,表示有效线程数runState,
表示是否正在运行、正在关闭等为了打包成一个int,
我们限制workerCount为(2^29 )-1(约 5 亿)个线程 --即最多可工作的线程最多为5亿多个
而不是 (2^31)-1(20 亿)个其他可表示的线程。
   2、
        线程池用ctl的低29位表示线程池中的线程数,高3位表示当前线程状态,后续假如想要增大这个值,可以将AtomicInteger改成AtomicLong。

     runState 提供主要的生命周期控制,取值: 111
     RUNNING:接受新任务并处理排队任务   000 
     SHUTDOWN:不接受新任务,但处理排队任务  001
     STOP:不接受新任务,不处理排队任务,并中断正在进行的任务  010
     TIDYING:所有任务都已终止  011
        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();
            重新检测线程池是否还在运行 如果没有运行 remove任务 并拒绝任务
            if (! isRunning(recheck) && remove(command))
                reject(command);
            
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        如果 添加缓存队列失败 那么新建线程数,
        如果线程数量大于最大线程数量,那么将使用拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }




检查是否可以根据当前池状态和给定界限(核心或最大值)添加新的工作线程。
如果是这样,则相应地调整工作人员数量,并且如果可能,将创建并启动一个新工作人员,
将 firstTask 作为其第一个任务运行。如果池已停止或有资格关闭,
则此方法返回 false。如果线程工厂在询问时未能创建线程,它也会返回 false。
如果线程创建失败,无论是由于线程工厂返回 null,还是由于异常
(通常是 Thread.start() 中的 OutOfMemoryError),我们都会干净利落地回滚。
参数:
firstTask – 新线程应该首先运行的任务(如果没有,则为 null)。
当线程少于 corePoolSize 时(在这种情况下我们总是启动一个),
或者当队列已满(在这种情况下我们必须绕过队列)时,
使用初始的第一个任务(在方法 execute() 中)
创建工作线程以绕过队列.最初空闲线程通常通过 prestartCoreThread 创建或替换其他垂死的工人。
core – 如果为 true,则使用 corePoolSize 作为绑定,
否则为 maximumPoolSize。 (此处使用布尔指标而不是值以确保在检查其他池状态后读取新值)。
返回:
如果成功则为真
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            
            int c = ctl.get();
            //获取正在运行状态的线程数
            int rs = runStateOf(c);

            检查 SHUTDOWN  =0 
            rs是否大于0 或者 运行线程是否为空 工作队列是否为空
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                查看线程池中线程的数量
                int wc = workerCountOf(c);
                 线程池中的数量是否大于容量或者wc是否大于核心线程数 返回失败
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                 利用cas去添加线程池中的数量 如果成功结束流程  跳出循环
                 如果失败重新获取C值
                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
            }
        }

        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 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) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ThreadPoolExecutor源码解析 的相关文章

  • Android Studio - 无法识别的 VM 选项“MaxPermSize=256m”

    我刚刚在 Elementary OS 0 3 Freya 上安装了 Android Studio 并使用终端运行它 然而 在我第一次启动时 显示一条错误消息 Gradle 测试 项目刷新失败 无法启动守护进程 这个问题可能是由 守护进程的配
  • Eclipse 构建 Android 应用程序:如何在编译时创建两个版本?

    我正在编写一个 Android 应用程序 并希望基于相同的代码创建两个版本 免费版本和高级版本 我有两个版本的一个代码库 具有各种运行时检查来启用或禁用某些功能 例如 public class MyAppContext extends Ap
  • 何时在 Springs @Configuration 中将 proxyBeanMethods 设置为 false?

    当查看 spring 自动配置时源代码 https github com spring projects spring boot tree master spring boot project spring boot autoconfigu
  • 如何用Spring进行只读和读写的数据库路由

    我正在研究 Spring 中的事务路由 但我的应用程序存在运行时问题 我有两个 MySQL 数据库 一个用于读取 一个用于读 写 但是我的路由配置不起作用 当我应用只读配置时 我没有成功 这是我的配置 pom xml
  • 如何在 El Capitan (OS X 10.11) 中设置 Android Studio?

    全新安装 El Capitan 10 11 尝试安装 Android Studio 版本 1 21 Error Android Studio was unable to find a valid JVM Please download it
  • 如何向正在运行的 Linux 进程发送 Ctrl-Break?

    我正在调试在 Sun 的 JDK 1 4 2 18 上运行的应用程序中的内存泄漏 该版本似乎支持命令行参数 XX HeapDumpOnCtrlBreak 这可能会导致 JVM 在遇到控制中断时转储堆 如何将其发送到 Linux 机器上的后台
  • 比 O(n) 更好的范围交集算法?

    范围交集是一个简单但不平凡的问题 已经回答过两次了 查找数字范围交集 https stackoverflow com questions 224878 find number range intersection 比较日期范围 https
  • JUnit 测试 Spymemcached 客户端

    我有一个类围绕spymemcached 客户端 我想编写一些JUnit 测试来测试getValue 和addKey 方法是否有效 问题是无法从测试服务器访问spymemcached 服务器 所以我想这里需要一些模拟 我的简化类看起来像这样
  • Android Studio 1.0.1 APK META-INF/DEPENDENCIES 中复制的重复文件

    我安装了 Android Studio 版本 1 0 1 并尝试将我的项目从 eclipse 导入到它 它给了我以下错误 Error Execution failed for task app packageDebug Duplicate
  • Hibernate 在更新集合时删除孤儿

    我发现从 Hibernate 中的集合中删除时 孤立记录不会被删除 我一定是做了一些简单的错误 这是 Hibernate 101 但我找不到它 鉴于以下情况 public class Book ManyToOne NotNull Autho
  • Spring数据异常处理

    我正在使用 Spring Data JPA 开发一个项目 我需要处理 JpaRepository 方法调用中的一些异常 在下面的代码中 我需要拦截主键违规错误 但无法直接捕获异常 就我而言 当发生此类异常时 存储库层 JpaReposito
  • Spring 如何在登录网址上设置动态前缀

    我有一个始终以动态前缀开头的 Spring 应用程序 这是因为我需要该前缀来进行一些内部配置 问题是 当我尝试设置登录页面时 无法传递该前缀并使其工作 如何为我的登录页面设置动态前缀 这是我的 AppController 的一部分 我在其中
  • 如何使用 JAVA 将本地图像而不是 URL 发送到 Microsoft Cognitive Face API

    我正在尝试使用 Microsoft 认知服务的 Face API 我想知道如何通过 Rest API 调用将本地图像发送到 Face API 并使用它请求结果JAVA 有人可以帮我解决这个问题吗 Microsoft 在其网站上提供的测试选项
  • 光线追踪三角形

    我正在用java编写一个光线追踪器 并且我能够追踪球体 但我相信我追踪三角形的方式有问题 据我了解 这是基本算法 首先确定射线是否与plane三角形已打开 剪裁所有点 使它们与三角形位于同一平面上 因此xy以平面为例 根据沿着新平面向任意方
  • 滚动文件实现

    我一直很好奇滚动文件是如何在日志中实现的 如何开始用任何语言创建一个文件写入类 以确保不超过文件大小 我能想到的唯一可能的解决方案是 write method size file size size of string to write i
  • 将 TextField 与 LibGDX 结合使用

    我正在使用 LibGDX 开发一款 Android 游戏 并且想要实现两个TextFields 登录到服务器 据我所知我需要使用Stage https libgdx badlogicgames com nightlies docs api
  • Spring MVC - 两次提供内容

    我已经花了一周时间寻找有关如何将内容服务器到我的网页的指导 两次 因为使用 Model 或 ModelAndView 切断内容一次可以工作 但如果用户再次与页面交互 我希望它加载更多内容同一页 Java Spring 后端方法 Get 有效
  • Spring-WS WSDL生成问题

    我正在尝试制作一个非常简单的 Web 服务 但在让 spring 生成正确的 wsdl 时遇到一些困难 我已尽力复制此示例春季教程 http static springsource org spring ws sites 2 0 refer
  • 在 Maven Shade 插件中包含依赖项

    我正在尝试使用 Apache 的 commons lang3 创建一个可部署的 jar 但是 我的 Hadoop 所在的 AWS 集群不包含此库 因此我收到了 classNotFoundException 我想我需要手动添加该依赖项 但我在
  • Java 压缩字符串

    我需要创建一个接收字符串并返回字符串的方法 防爆输入 AAABBBCCC 防爆输出 3A4B2C 好吧 这很尴尬 我在今天的面试中无法做到这一点 我正在申请初级职位 现在 我在家尝试制作一些静态工作的东西 我的意思是 不使用循环有点无用 但

随机推荐

  • muduo 架构解析

    muduo是一个基于Reactor模式的C 网络库 它采用非阻塞I O模型 基于事件驱动和回调 我们不仅可以通过muduo来学习linux服务端多线程编程 还可以通过它来学习C 11 Reactor是网络编程的一般范式 我们这里从react
  • RockyLinux9.1环境初始化

    下载镜像 https rockylinux org download 基础设置 硬件配置 系统配置 系统初始化 配置网络 配置网络 etc NetworkManager system connections ens160 nmconnect
  • 要言不烦先行指标与滞后指标的12个要点

    先行指标 leading indicator 是在结果发生之前对结果具有预测作用的度量数据 又称为超前指标 预测性指标 先导指标 领先指标 行为指标 过程指标等 滞后指标 lagging indicator 是对最终结果的度量数据 反映的是
  • jar包读取资源文件报错:找不到资源文件(No such file or directory)

    1 遇到问题 1 Maven项目开发阶段正常运行 Java程序可以读取配置文件 public class Main public static void main String args throws Exception Main read
  • python——返回函数、闭包函数、偏函数

    文章目录 1 返回函数 2 闭包函数 3 偏函数 1 返回函数 函数的返回值也可以是函数 def food name 外函数 def prepare 内函数 print f name 制作步骤 备菜 内部函数可以使用外部函数的变量 def
  • 看涨期权计算例题(期权案例计算)

    看涨期权又称认购期权 买进期权 买方期权 买权 延买期权 或 敲进 是指期权的购买者拥有在期权合约有效期内按执行价格买进一定数量标的物的权利 下文为大家科普看涨期权计算例题 期权案例计算 本文来自 期权酱 当看涨期权 Call Option
  • 同步资源失败,未得到同步资源的授权,请停止运行后重新运行,并注意手机上的授权提示

    问题描述 提示 HBuilderX 真机调试异常 这个问题困惑了我好几天 终于解决了 同步资源失败 未得到同步资源的授权 请停止运行后重新运行 并注意手机上的授权提示 解决方案 提示 使用adb删除对应包名 问题未解决 尝试重新启动手机再运
  • 第十届蓝桥杯省赛C++B组 数列求值

    试题 C 数列求值 本题总分 10 分 问题描述 给定数列 1 1 1 3 5 9 17 从第 4 项开始 每项都是前 3 项的和 求第 20190324 项的最后 4 位数字 答案提交 这是一道结果填空的题 你只需要算出结果后提交即可 本
  • 以数据为中心的标记语言-->yaml

    目录 一 yaml 介绍 二 yaml 基本语法 三 数据类型 1 字面量 2 对象 3 数组 四 yaml 应用实例 1 需求 2 需求图解 3 代码实现 五 yaml 使用细节 一 yaml 介绍 YAML 是 YAML Ain t a
  • C++ 之 explicit,mutable,volatile 浅析

    explicit 放在构造函数前面可以阻止构造函数的隐式类型转换 这样可以避免不必要的错误 代码用例 public static explicit operator RMB float f uint yuan uint f uint jia
  • STM32 ADC详解

    目录 01 ADC简介 02 STM32的ADC外设 03 STM32ADC框图讲解 04 触发源 05 转换周期 06 数据寄存器 07 中断 08 电压转换 09 电路图设计 10 代码设计 01 ADC简介 ADC是Analog to
  • 给Linux扩充swap分区

    https blog csdn net u011109881 article details 73694700
  • 【计算机视觉

    文章目录 一 检测相关 1篇 1 1 SegmentAnything helps microscopy images based automatic and quantitative organoid detection and analy
  • vue-cli3项目打包后自动化部署到服务器

    一 安装 scp2 npm install scp2 save dev 二 写好脚本 例如 upload js 下面任选一个即可 位置和 package json平级即可 简略版 use strict 引入scp2 var client r
  • ctfhub 基础认证

    1 打开题目环境 2 点击click跳出来一个登录弹窗 随便输入用户名和密码登录试试 没有返回任何有用信息 3 查看附件 得到一堆密码 应该是要直接爆破 4 点击click抓包后发送到repeater模块 重新发包得到 Do u know
  • python global local nonlocla

    目录 What is the global keyword Rules of global Keyword Use Global Example 1 Accessing global Variable From Inside a Funct
  • redis-cli的安装

    1 下载redis cli 2 解压缩 3 简易配置 4 查看redis Cli的使用说明 5 常规连接指令 redis cli h 目标主机ip地址 p 端口号
  • {System.InvalidOperationException: 未在本地计算机上注册“Microsoft.Ace.OleDb.12.0”提供程序。

    System InvalidOperationException 未在本地计算机上注册 Microsoft Ace OleDb 12 0 提供程序 在 System Data OleDb OleDbServicesWrapper GetDa
  • 前端面试-HTML5篇

    链接 https www nowcoder com questionTerminal 来源 牛客网 Question 6 描述一下 cookies sessionStorage 和 localStorage 的区别 cookit是网站为了标
  • ThreadPoolExecutor源码解析

    ThreadPoolExecutor源码解析 一 新建线程池的是构造方法 public ThreadPoolExecutor int corePoolSize int maximumPoolSize long keepAliveTime T