[Java]ThreadPoolExecutor源码分析

【java】java中的线程池 ThreadPoolExecutor源码分析 文章目录1.概述4.源码4.1 关键属性4.2 构造函数4.4 状态控制4.5 ThreadLocalMap4.6 execute方法源码分析4.7 addWorker方法源码分析4.8 Worker4.9 getTask方法源码分析4.10 processWorkerExit方法源码分析4.11 tryTerminate方法源码分析4.12 shutdown方法源码分析5.理解可重入锁mainLock成员变量6.reject方法源码分析7.钩子方法分析8. 其他方法小结 1.概述 首先可以看看 【高并发】 阅读详情

ThreadPoolExecutor 使用线程池来处理提交的线程任务,该类主要用来处理两大问题:1.提升大量异步任务的执行性能。2.同时也提供任务完成情况信息。


通常我们要使用ThreadPoolExecutor 可以通过其构造类方法,或者使用Executors工厂类的到ThreadPoolExecutor。

如果使用构造类方法,我们需要懂其中几个必要的构造参数:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }


corePoolSize:最小存活Worker数量,这里得说一个time out机制,每一个Worker都会有生存时间,过了生存时间就会被回收掉。一般情况下corePoolSize个Worker不会被回收,如果允许回收,就要设置allowCoreThreadTimeOut。

maximumPoolSize:线程池的极限大小

keepAliveTime:以nanoseconds表示的空闲Worker回收时间,allowCoreThreadTimeOut设置为true的情况下,包括空闲 core Thread也是被回收掉的
workQueue:这是用来保存待执行任务队列,并且会把任务给予Worker.

threadFactory:顾名思义,这是用来产生包裹Runnable对象的线程工厂。

handler:当提交任务数超过maximumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理。

一般情况下我们还是使用Executors来产生ThreadPoolExecutor

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }


一般我们添加任务的时候都是使用 public void execute(Runnable command)

    /**
     * Executes the given task sometime in the future.  The task
     * may execute in a new thread or in an existing pooled thread.
     *
     * If the task cannot be submitted for execution, either because this
     * executor has been shutdown or because its capacity has been reached,
     * the task is handled by the current {@code RejectedExecutionHandler}.
     *
     * @param command the task to execute
     * @throws RejectedExecutionException at discretion of
     *         {@code RejectedExecutionHandler}, if the task
     *         cannot be accepted for execution
     * @throws NullPointerException if {@code command} is null
     */
    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.
         */
        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);
    }

其中注释也很明确的分为3步:

1.如果少于corePoolSize的Worker在执行任务,那么就创建任务

2.否则检查Executor是否在执行然后将任务加入workQueue,其中进行了双重检查,如果Executor此时不在Running状态,那么进行回滚。

3.如果不能加进workQueue那么新建线程,不能的话就拒绝任务。



可以看出addWorker是关键操作,那么addWorker到底做了些什么呢?

   /**
     * Checks if a new worker can be added with respect to current
     * pool state and the given bound (either core or maximum). If so,
     * the worker count is adjusted accordingly, and, if possible, a
     * new worker is created and started, running firstTask as its
     * first task. This method returns false if the pool is stopped or
     * eligible to shut down. It also returns false if the thread
     * factory fails to create a thread when asked.  If the thread
     * creation fails, either due to the thread factory returning
     * null, or due to an exception (typically OutOfMemoryError in
     * Thread.start()), we roll back cleanly.
     *
     * @param firstTask the task the new thread should run first (or
     * null if none). Workers are created with an initial first task
     * (in method execute()) to bypass queuing when there are fewer
     * than corePoolSize threads (in which case we always start one),
     * or when the queue is full (in which case we must bypass queue).
     * Initially idle threads are usually created via
     * prestartCoreThread or to replace other dying workers.
     *
     * @param core if true use corePoolSize as bound, else
     * maximumPoolSize. (A boolean indicator is used here rather than a
     * value to ensure reads of fresh values after checking other pool
     * state).
     * @return true if successful
     */
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                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;
    }
这里需要明白一点即Executor的运行状态

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这里用AtomicInteger来表征运行状态信息,Interger为32位数,其中前3位表示运行状态,后29表示所有的Worker数目

    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 bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

COUNT_BITS表示位移位数 29

runStateOf(int c) 返回前3位数的值,运行状态总共有5种:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED.

RUNNING:(0b111.....):接受任务并且处理workQueue中的任务

SHUTDOWN(0b000.....):不接受任务但是处理workQueue中的任务

STOP(0b001......):不接受任务不处理workQueue中的任务,同时中断执行中的任务

TIDYING(0b010.....):所有任务终止,workCount=0,此时会调用terminated() hook

TERMINATED(0b011.....):terminated()结束。


        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                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
            }
        }

addWorker中这一部分

if (rs >= SHUTDOWN &&! (rs == SHUTDOWN &&firstTask == null && ! workQueue.isEmpty())) return false;
rs>=SHUTDOWN即非RUNNING状态,之前也看到了各种状态的二进制形式只有RUNNING<0;
并且rs状态不为SHUTDOWN或者firstTask不为null或者workQueue为空的情况下失败。

如果Worker数已达到上限则失败

否则增加Worker计数,也就是允许创建新的Worker来执行任务。


这里不得不提到一个关键的内部类Worker,Worker类如同名字一样是对执行线程任务的封装。

Worker实现了Runnable接口,也就是是实现了run()方法。

        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
这里将任务传递给Worker,同时产生包裹线程。



值得一提的是,通过BlockingQueue来控制addWorker().





SAP数据导入避坑指南:财务期初上线实操全流程(含常见错误排查) 本文详细解析了SAP财务期初数据导入的全流程,包括数据准备、主数据标准化、余额与未清项导入等关键步骤,并提供了常见错误排查和解决方案。特别强调了过渡科目和存货价值的处理技巧,帮助企业在SAP系统上线时避免数据导入陷阱,确保财务数据的准确性和系统平稳运行。 阅读详情

相关推荐

G2O (General Graph Optimization)入门及简单使用

g2o全称是General Graph Optimization,也就是图优化,我们在做SLAM后端或者更加常见的任何优化问题(曲线拟合)都可以使用G2O进行处理。

晓晨的博客 1万+

线程池ThreadPoolExecutor源码剖析

饭店(线程池)-- 厨子(线程)-- 人多先排队(阻塞队列)-- 招厨子I(创建最大线程数) – 今日客满(拒绝)线程池方式 (Java提供了构建线程池的方式)[可以实现Runnable 和 Callable 功能]有时间再去查看getTask方法。processWorkerExit线程执行完毕的后续处理。ThreadPoolExecutor 中的属性(核心成员变量)标识。实现Callable (与Runnable区别…继承Thread (也实现了Runnable)看runWorker方法。

码字不易,大家的支持就是我坚持下去的动力 417

scDeepCluster_pytorch代码解读与文章理解

论文:Clustering single-cell RNA-seq data with a。

qq_34179307的博客 913

Java 线程池之 ThreadPoolExecutor 源码分析

Java 线程池源码分析(基于JDK1.8): ThreadPoolExecutor 是Java最常用的线程池,今天来分享下源码分析,以下是ThreadPoolExecutor具体类继承关系,以及方法详情 我们看到ExecutorService接口,提供了submit(Runnable)、submit(Runnable,T)、submit(Callable&lt;T&gt;)三个接口方法,...

432

Vue3多级路由缓存失效的深度解析与实战解决方案

本文深度解析了Vue3项目中多级路由下keep-alive缓存失效的根本原因,并提供了三种实战解决方案:通过路由扁平化根治问题、利用状态管理精细模拟缓存效果,以及借助keep-alive-vue3插件快速修复。文章旨在帮助开发者彻底理解Vue Router与keep-alive的协作机制,从而根据项目情况选择最佳策略,有效解决页面状态丢失的难题。

weixin_29015801的博客 285

Zynq7000混合启动方案:QSPI Flash引导与eMMC ext4文件系统部署实战

本文详细介绍了Zynq7000平台的混合启动方案,结合QSPI Flash引导与eMMC ext4文件系统部署,解决嵌入式Linux系统启动可靠性与大容量存储需求。通过硬件连接配置、分区规划、Petalinux工程配置及实战部署步骤,帮助开发者高效实现系统优化,提升启动速度和文件系统性能。

weixin_29190169的博客 447

线程池(ThreadPoolExecutor)源码解析

目录一、线程池概念介绍1.1、什么是线程池1.2、线程池工作流程1.3、线程池的代码用例二、源码解析——构造函数2.1、线程池的构造函数及类的继承关系2.2、Executors提供的线程池模板2.3、拒绝策略的实现2.4、任务队列BlockingQueue的实现三、 源码解析——execute(Runnable command)3.1、流程概述 一、线程池概念介绍 1.1、什么是线程池 池化技术其实在技术生态圈中是比较常见的,比如:对象池、连接池等等。 为什么会有线程池这个东西存在呢?其实原因跟其他池化技

小志的博客 8747

超详细的YOLOv8安装与测试指南:让计算机视觉任务变得简单

python3+、Anaconda3或Miniconda3 Ultralytics提供了多种安装方法,包括pip、conda和Docker。通过pip包安装最新稳定版的YOLOv8,或者克隆Ultralytics GitHub仓库以获取最新版本。Docker可用于在隔离容器中执行包,避免本地安装。 最简单直接的方式就是直接使用pip方法直接安装。 也可以直接从GitHub仓库安装包。如果你想要最新的开发版本,这可能会很有用。确保你的系统上安装了Git命令行工具

CDBmax的博客 1万+

线程池ThreadPoolExecutor源码分析

继承Thread类通过继承Thread类并重写其run方法来创建线程。具体步骤包括定义Thread类的子类,在子类中重写run方法以实现线程的具体逻辑,然后创建子类的实例并调用其start方法来启动线程。实现Runnable接口通过实现Runnable接口并重写其run方法来创建线程。这种方式相较于继承Thread类更为灵活,因为Java不支持多重继承,而实现接口则没有这个问题。

qq_44286009的博客 939

告别混乱代码!在Arduino IDE中管理多文件项目的5个必备技巧

本文分享了在Arduino IDE中管理多文件项目的5个必备技巧,帮助开发者告别混乱代码。通过代码分割和模块化设计,提升编译效率、降低内存占用,并实现功能解耦。重点介绍了头文件设计、CPP文件实现、调试技巧和工业级目录结构,让你的Arduino项目像乐高积木一样清晰易维护。

fun88的博客 360

【STM32】STM32 CANFD外设的几种消息接收过滤模式

在控制器区域网络(CAN)系统中,正确配置消息过滤器对于有效管理网络通信至关重要。特别是在使用Flexible Data-rate Controller Area Network(FDCAN)时,选择合适的过滤器类型可以大大提高网络效率和消息处理速度。

林愿留的博客 6225

简单上手线程池

简单上手线程池

x2306402025的博客 281

防护装备穿戴检查:基于YOLOv8与UI界面的深度学习应用

通过结合YOLOv8模型与UI界面,本系统实现了防护装备穿戴检查的自动化。该系统能够快速识别工作人员是否正确佩戴了所需的防护装备,帮助提高安全管理水平。未来,随着技术的发展,防护装备穿戴检查系统将在更多领域得到应用,如工业现场、建筑工地等,实现更加精确和高效的实时监控。

YOLO 354

java线程池ThreadPoolExecutor原理源码分析

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

qq_34735262的博客 1848

android 蓝牙通讯demo

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

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Java ThreadPoolExecutor线程池原理及源码分析

一、源码分析(基于JDK1.6) ThreadExecutorPool是使用最多的线程池组件,了解它的原始资料最好是从从设计者(Doug Lea)的口中知道它的来龙去脉。在Jdk1.6中,ThreadPoolExecutor直接继承了AbstractExecutorService,并层级实现了ExecutorService和Executor接口。 1.Executor Executor是

Schelor的专栏 3034

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Java ThreadPoolExecutor 源码分析

1.背景    了解过Java线程池的同学一定对Executors不陌生,我们可以用它的静态方法很快的创建出我们需要的线程池,比如:我们需要一个固定数目的线程池,那么可以这样:     Executors.newFixedThreadPool(5)  创建一个只有5个线程的线程池。也可以通过:     Executors.newCachedThreadPool()  创建一个没有数量限制的线程池。...

爱技术,爱生活 319

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Java并发之线程池ThreadPoolExecutor源码分析学习

文章目录线程池学习一:线程池的优势二:线程池的创建三:ThreadPoolExecutor源码分析3.1 AtomicInteger ctl1.CAPACITY2.runState3.部分方法介绍3.2 核心方法源码分析1.execute(Runnable command)方法2.addWorker(Runnable firstTask, boolean core)3.addWorkerFaile...

秋夜无霜 235

Java并发之线程池ThreadPoolExecutor源码分析学习,java面试题,java高级笔试题

写在最前面,我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家。扫码加微信好友进【程序员面试学习交流群】,免费领取。也欢迎各位一起在群里探讨技术。 线程池学习 以下所有内容以及源码分析都是基于JDK1.8的,请知悉。 我写博客就真的比较没有顺序了,这可能跟我的学习方式有关,我自己也觉得这样挺不好的,但是没办法说服自己去改...

ccc_ccc8的博客 302

java开发之ThreadPoolExecutor源码分析

线程池的状态 只有了解线程池的几个状态,才能读懂它的核心源码。所以先说说这几个状态 running:为线程池初始化时的默认状态,此状态会接收任务进行处理 shutdown: 该状态下的线程池不接收任何任务,但会等待正在运行的任务执行完。通常调用shutdown() 方法完成设置 stop: 该状态的线程池不接收任何任务,同时​​java培训​​不会等待正在运行的任务执行完毕。通常调用shutdownNow() 方法完成设置 tidying:该状态下的线程池内,没有任何线程和任务 terminat

m0_58371965的博客 168

Java并发编程:ThreadPoolExecutor类及方法源码分析

ThreadPoolExecutor是jdk自带线程池实现类,现有的Executors工具类实现的几种线程池核心都是调用ThreadPoolExecutor类。ThreadPoolExecutor在jdk1.7及以后做了部分修改,本文以JDK1.8为准。1 构造函数ThreadPoolExecutor类共有4个构造函数,其他三个构造函数都是调用下参数最全的一个,下面只介绍参数最全的的一个。 publ

ahsxsk的学习之路 742

Java线程池ThreadPoolExecutor源码分析

Java线程池ThreadPoolExecutor源码分析

Knight 650

java基础--ThreadPoolExecutor源码分析

jdk1.8 ThreadPoolExecutor源码分析

zclzhangcl的博客 888

Java 线程池 ThreadPoolExecutor 源码分析

线程池能够对线程进行有效的管理, 复用和数量上限的限制, 所以比起原始的 new Thread().start() 这种创建并启动线程的方式, 线程池的效率和性能都更好. Java 中的线程池是用 ThreadPoolExecutor 类来表示的. 我们今天就结合该类的源码来分析一下这个类内部对于线程的创建, 管理以及后台任务的调度等方面的执行原理. 我这里分析的是 JDK 1.8 的源码.

clevergump的专栏 1万+

java 线程池 源码_Java并发之线程池ThreadPoolExecutor源码分析学习

线程池学习以下所有内容以及源码分析都是基于JDK1.8的,请知悉。​ 我写博客就真的比较没有顺序了,这可能跟我的学习方式有关,我自己也觉得这样挺不好的,但是没办法说服自己去改变,所以也只能这样想到什么学什么了。​ 池化技术真的是一门在我看来非常牛逼的技术,因为它做到了在有限资源内实现了资源利用的最大化,这让我想到了一门课程,那就是运筹学,当时在上运筹学的时候就经常做这种类似的问题。​ ...

weixin_32301287的博客 204
上一篇: [Java]HashMap源码分析
下一篇: [linux]tee命令以及实现
HeapStackk
博客等级 码龄12年 11粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值