并发编程(五)-ExecutorService源码分析

一、ExecutorService是什么?


ExecutorService 是 Java 中的一个接口,它扩展了 Executor 接口,并提供了更多的方法来处理多线程任务。它是 Java 中用于执行多线程任务的框架之一,可以创建一个线程池,将多个任务提交到线程池中执行。ExecutorService 接口提供了许多方法,如 shutdown()、shutdownNow()、submit()、execute()、invokeAll() 等,可以更方便地提交任务、执行任务、关闭线程池等操作。同时,ExecutorService 还提供了线程池的管理和监控功能,可以更好地控制和管理线程池中的线程。在实际应用中,ExecutorService 通常与 Callable 和 Future 接口一起使用,可以实现更加灵活和高效的多线程编程。

二、ExecutorService作用


ExecutorService 是 Java 中用于执行多线程任务的框架,它允许我们创建一个线程池,将多个任务提交到线程池中执行。它的主要作用是优化线程的创建和销毁过程,提高程序的效率和性能。通过 ExecutorService,我们可以更好地控制线程的数量、避免线程阻塞和死锁,从而更好地管理和调度线程,提高应用程序的并发处理能力。在实际开发中,ExecutorService 可以用于处理大量的并发请求、分批处理大数据等多线程场景。

三、ExecutorService原理


ExecutorService 的实现原理主要是基于线程池的概念。当我们创建一个 ExecutorService 对象时,实际上就是创建了一个线程池。线程池中包含了若干个线程,这些线程可以执行我们提交的任务。当线程池中的线程空闲时,它们会等待任务的到来,一旦有任务提交,就会从线程池中选择一个空闲的线程执行该任务。如果线程池中的线程都在执行任务,那么新的任务就会被暂时放在任务队列中,等待线程空闲时再来执行。

在 ExecutorService 的实现中,任务的提交和执行是异步的,也就是说,我们提交任务时不会阻塞当前线程,而是将任务交给线程池中的线程去执行。当任务执行完成后,线程会将执行结果返回给我们。同时,我们可以通过调用 ExecutorService 的方法来管理和控制线程池,如增加或减少线程数量、关闭线程池等。总之,ExecutorService 的实现原理是基于线程池的概念,通过管理和调度线程,提高程序的效率和性能,同时避免线程阻塞和死锁等问题,从而更好地管理和调度线程,提高应用程序的并发处理能力。

四、源码分析


4.1 Diagrams 总体概览

4.2 方法

线程池中有五种状态,分别是 RUNNING、STOP、SHUTDOWN、TIDYING 和 TERMINATED。它们的含义和区别如下:

  • RUNNING:表示线程池处于运行状态,接受新的任务并且处理任务队列中的任务,直到线程池被显式地关闭。

  • SHUTDOWN:表示线程池处于关闭状态,不再接受新的任务,但是会尝试执行任务队列中的任务,直到任务队列为空。在任务队列为空后,线程池会进入 TIDYING 状态。

  • STOP:表示线程池处于停止状态,不再接受新的任务,也不会继续执行任务队列中的任务。此时,线程池会尝试中断正在执行的任务,并立即返回任务队列中的所有任务。在任务队列为空后,线程池会进入 TIDYING 状态。

  • TIDYING:表示线程池正在进行线程回收的操作,此时线程池中的所有任务都已经执行完成,而线程池中的线程也已经被销毁。在线程回收完成后,线程池会进入 TERMINATED 状态。

  • TERMINATED:表示线程池已经完全终止,不再接受任何任务,也不会执行任何任务。此时,线程池中的所有线程都已经被销毁,线程池对象也可以被垃圾回收。

总之,这五种状态代表了 ThreadPoolExecutor 在不同时间点的不同状态,分别表示线程池的运行状态、关闭状态、停止状态、回收状态和终止状态。它们的区别在于线程池在不同状态下的行为和状态转换。

4.2.1 shutdown()方法

ExecutorService 中的 shutdown() 方法是用于关闭线程池的,它会停止接受新的任务,并尝试将已经提交但尚未执行的任务执行完成。在调用 shutdown() 方法后,线程池不再接受新的任务,而是等待之前提交的任务执行完成后关闭线程池。具体来说,shutdown() 方法会将线程池的状态设置为 SHUTDOWN,然后中断所有没有开始执行的任务,并尝试将已提交但未开始执行的任务从任务队列中移除,最后等待所有正在执行的任务执行完成后关闭线程池。如果线程池中的任务是通过 Future 返回结果的,那么在调用 shutdown() 方法后,我们可以通过 Future 的 isDone() 方法来判断任务是否执行完成。需要注意的是,shutdown() 方法并不会强制终止任务的执行,它只会尝试将任务执行完成。如果任务本身存在死循环或者其他无法正常结束的情况,那么线程池可能无法正常关闭。如果想要强制终止所有任务的执行,可以使用 shutdownNow() 方法。

总之,shutdown() 方法是 ExecutorService 中用于关闭线程池的方法,它会尝试将已提交但未执行的任务执行完成后关闭线程池,是一个比较温和的关闭方式。

4.2.2 shutdownNow()方法

ExecutorService 中的 shutdownNow() 方法是用于强制关闭线程池的,它会尝试立即停止所有正在执行的任务,并返回那些未执行的任务列表。在调用 shutdownNow() 方法后,线程池会尝试中断所有正在执行的任务,并将任务队列中未执行的任务返回给调用者。如果线程池中的任务是通过 Future 返回结果的,那么可以通过 Future 的 isDone() 方法来判断任务是否执行完成。需要注意的是,shutdownNow() 方法并不是强制终止任务的执行,它只是尝试中断正在执行的任务。如果任务本身存在死循环或者其他无法正常结束的情况,那么线程池可能无法正常关闭。因此,在使用 shutdownNow() 方法时,需要谨慎考虑,避免对任务造成不可逆的影响。

总之,shutdownNow() 方法是 ExecutorService 中用于强制关闭线程池的方法,它会尝试立即停止所有正在执行的任务,并返回未执行的任务列表,是一种比较强硬的关闭方式。

4.2.3 isShutdown()方法

ExecutorService 中的 isShutdown() 方法是用于判断线程池是否已经关闭的,它返回一个 boolean 类型的值,如果线程池已经关闭,则返回 true,否则返回 false。当线程池被关闭后,它就不能再接受新的任务,但是它可能还有一些已经提交但还未执行完成的任务。调用 isShutdown() 方法可以判断当前线程池是否已经关闭,从而可以根据需要进行进一步的处理,如等待未执行的任务执行完成后再关闭线程池等。需要注意的是,isShutdown() 方法只能判断线程池是否已经关闭,而不能判断线程池是否已经完全终止。如果要判断线程池是否已经完全终止,可以使用 isTerminated() 方法。isTerminated() 方法返回一个 boolean 类型的值,如果线程池已经完全终止,则返回 true,否则返回 false。

总之,isShutdown() 方法是 ExecutorService 中用于判断线程池是否已经关闭的方法,可以根据返回值来判断线程池是否已经关闭,从而进行进一步的处理。

4.2.4 isTerminated()方法

ExecutorService 接口中的 isTerminated() 方法用于判断线程池是否已经终止。如果线程池已经被显式地关闭,并且所有任务都已经执行完成并且所有线程都已经被销毁,那么 isTerminated() 方法会返回 true;否则,返回 false。isTerminated() 方法的实现原理是通过检查线程池的状态来判断线程池是否已经终止,具体来说,它会判断线程池是否处于 TERMINATED 状态,如果是,就返回 true;否则,返回 false。在使用 ExecutorService 管理线程池时,可以使用 isTerminated() 方法来判断线程池的状态,从而决定是否需要等待线程池执行完所有任务,并且所有线程都被销毁之后再进行下一步操作。常见的做法是在关闭线程池后,使用 isTerminated() 方法轮询线程池的状态,直到返回 true 为止。

4.2.5 awaitTermination(long timeout, TimeUnit unit) 方法

ExecutorService 接口中的 awaitTermination(long timeout, TimeUnit unit) 方法用于等待线程池中所有任务执行完成,并且所有线程都被销毁。它会阻塞当前线程,直到线程池中的所有任务都已经执行完成,并且所有线程都已经被销毁,或者等待超时(由 timeout 和 unit 指定)。如果在指定的等待时间内所有任务都已经执行完成并且所有线程都已经被销毁,方法会返回 true;否则,返回 false。也就是说,如果线程池在等待超时前已经终止,方法会返回 true;否则,返回 false。

在使用 ExecutorService 管理线程池时,可以使用 awaitTermination(long timeout, TimeUnit unit) 方法来等待线程池中的所有任务执行完成,并且所有线程都被销毁。常见的做法是在关闭线程池后,先调用 shutdown() 方法关闭线程池,然后调用 awaitTermination(long timeout, TimeUnit unit) 方法等待线程池中的所有任务执行完成,并且所有线程都被销毁。如果需要在等待超时后继续执行下一步操作,可以根据返回值来决定是否调用 shutdownNow() 方法强制中断线程池中的任务并销毁所有线程。

4.2.6 <T> Future<T> submit(Callable<T> task)方法

在 ExecutorService接口中,<T> Future<T> submit(Callable<T> task)方法用于向线程池提交一个需要返回结果的任务,该方法接受一个 Callable<T> 类型的参数,该参数表示要执行的任务,Callable<T> 接口中的 T表示任务执行后的返回值类型。 该方法将任务提交到线程池中,并返回一个 Future<T> 对象,用于获取任务执行的结果。在任务执行完毕后,可以通过调用 Future<T>对象的 get()方法来获取任务执行的结果,如果任务还没有执行完,则 get()方法会阻塞等待任务执行完毕并获取结果。 如果任务执行过程中发生异常,get()方法会抛出相应的异常,可以通过捕获异常来进行相应的处理。

4.2.7 List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)方法

在 ExecutorService 接口中,List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) 方法用于向线程池提交一组任务,并等待所有任务执行完毕并返回结果。该方法接受一个 Collection<? extends Callable<T>> 类型的参数,该参数表示要执行的任务集合,Callable<T> 接口中的 T表示任务执行后的返回值类型。 该方法将所有任务提交到线程池中,并返回一个 List<Future<T>>对象,用于获取所有任务执行的结果。在所有任务执行完毕后,可以通过循环遍历 List<Future<T>> 对象来获取每个任务执行的结果,如果任务还没有执行完,则 get()方法会阻塞等待任务执行完毕并获取结果。 如果任务执行过程中发生异常,get() 方法会抛出相应的异常,可以通过捕获异常来进行相应的处理。

4.2.8 <T> T invokeAny(Collection<? extends Callable<T>> tasks) 方法

ExecutorService.invokeAny()方法会阻塞直到至少有一个任务完成,并返回其执行结果。如果其中一个任务抛出异常,该异常将被传递给调用者。如果所有任务都抛出了异常,则抛出 ExecutionException异常。

此方法适用于需要执行多个任务,只需要获取其中任意一个任务执行成功的结果的场景。此方法例如,在多个引擎上搜索某个关键字,只要有一个搜索引擎返回了结果,就可以将其作为最终结果返回给用户。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++进阶】十一、哈希的应用---布隆过滤器(二)

目录 一、布隆过滤器提出 二、布隆过滤器概念 三、布隆过滤器实现 3.1 布隆过滤器的插入 3.2 布隆过滤器的查找 3.3 布隆过滤器的删除 3.4 完整代码 四、布隆过滤器优点 五、布隆过滤器缺陷 一、布隆过滤器提出 在注册账号设置昵称的时候&#xff0c;有些软件要求每个…

元宇宙、区块链 通俗易懂

什么是区块链&#xff1f;比特币挖矿是什么&#xff1f;元宇宙是什么&#xff1f;Web(万维网)的三权化进化&#xff1a;基于此&#xff0c;介绍下“元宇宙”。1992年&#xff0c;美国作家史蒂芬森在《雪崩》一书中首次提出了“元宇宙(Metaverse)”的概念。元宇宙实际上就是一种…

【MIT 6.S081】Lab3: page tables

PgtblPrint a page tableA kernel page table per processSimplify copyin/copyinstr本Lab简单优化了系统的页表功能&#xff0c;使得程序在内核态时可以直接解析用户态的指针。笔者用时约8hPrint a page table 第一部分是为系统添加一个打印给定页表的函数vmprint&#xff0c…

C语言-程序环境和预处理(2)

文章目录预处理详解1.预定义符号2.#define2.1#define定义的标识符2.2#define定义宏2.3#define替换规则注意事项&#xff1a;2.4#和###的作用##的作用2.5带副作用的宏参数2.6宏和函数的对比宏的优势&#xff1a;宏的劣势&#xff1a;宏和函数的一个对比命名约定3.undef4.条件编译…

centos系统/dev/mapper/centos-root目录被占满的解决方式

最近在做虚拟机部署docker微服务时&#xff0c;发现磁盘内存占满&#xff0c;无法进行操作。open /var/lib/dpkg/info/libc6:amd64.templates: no space left on device接下来就写下我在备份虚拟机上如何解决根目录被占满的问题&#xff1a;1、查看虚拟机磁盘使用情况df -h可以…

D - 统计子矩阵 (双指针+前缀和+降维处理)

D - 统计子矩阵 &#xff08;双指针前缀和降维处理&#xff09; 1、问题 D - 统计子矩阵 2、分析 代码 &#xff08;1&#xff09;纯暴力做法&#xff1a; 这个做法就很简单了&#xff0c;我们直接枚举所有的子矩阵&#xff0c;然后在对每一个子矩阵内部的元素逐一累加起…

【计算机二级】综合题目

计算机二级python真题 文章目录计算机二级python真题一、《大学慕课 两问 》二、综合应用题——价值链三、基本操作题 ——信息输出一、《大学慕课 两问 》 附件中的文件data.txt 是教育部爱课程网中国大学MOOC平台的某个 HTML页面源文件,里面包含了我国参与MOOC建设的一批大学…

STM32之点亮一个LED小灯(轮询法)

目录 一、初始化GPIO口 二、按键点亮LED灯&#xff08;轮询法&#xff09; 一、初始化GPIO口 1、点亮LED小灯前&#xff0c;需要先初始化GPIO口 HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) GPIO_TypeDef *GPIOx&#xff1a; //指初始化GPIO…

libvirt零知识学习5 —— libvirt源码编译安装(3)

接前一篇文章libvirt零知识学习4 —— libvirt源码编译安装&#xff08;2&#xff09; 在上篇文章及上上篇文章中构建libvirt的时候遇到了一个问题“ERROR: Problem encountered: YAJL 2 is required to build QEMU driver”。上篇文章讲到即使安装了相应的YAJL库仍然不能解决问…

HC小区管理系统window系统安装教程

实操视频 HC小区管理系统局域网window物理机部署教程_哔哩哔哩_bilibili 一、下载安装包 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1XAjxtpeBjHIQUZs4M7TsRg 提取码&#xff1a;hchc 或者 123盘 hc-window.zip官方版下载丨最新版下载丨绿色版下…

12个 Python 装饰器让代码cleaner

0 — 引言装饰器能在在不影响质量的情况下用更少的代码做更多的事情。Python 装饰器是强大的工具&#xff0c;可帮助你生成干净、可重用和可维护的代码。我等了很久才想了解这些抽象&#xff0c;现在我已经有了扎实的理解&#xff0c;本篇是为了帮助你也掌握这些对象背后的概念…

uni-app+uView如何轮播图滑动时改变背景颜色和导航栏颜色

今儿的创作欲很高涨哈 &#x1f604; 这也是在群里看到的&#xff0c;群友问如何在滑动&#xff08;或者自动滑动&#xff09;的时候背景颜色能跟着变 正好之前做过这个需求&#xff0c;也分享一下 首先&#xff0c;页面的组成分为三部分&#xff1a; 自定义navbar 页面背景轮…

JavaSE进阶之(十六)枚举

十六、枚举16.1 背景16.2 枚举类型16.3 EnumSet 和 EnumMap01、EnumSet02、EnumMap16.1 背景 在 Java 语言中还没有引入枚举类型之前&#xff0c;表示枚举类型的常用模式是声明一组 int 类型的常量&#xff0c;常常用的就是&#xff1a; public static final int SPRING 1; …

ElementUI学习笔记

目录 一、简单介绍 二、安装 1、下载 2、引入 三、布局 1、简介 2、使用 3、好处 四、布局容器 1、常见排布 2、调整样式 五、按钮 1、简单引用 2、改变样式 3、加载中效果 六、表格 1、简单使用 2、样式修改 七、对话框 1、简单使用 2、添加自定义内容 3、…

7个最受瞩目的 Python 库,提升你的开发效率

当今时代&#xff0c;数据分析和处理已经成为了各行各业中不可或缺的一环。Python作为一种非常流行的编程语言&#xff0c;为我们提供了许多强大的工具和库来处理不同类型的数据。 在这篇文章中&#xff0c;我将向您介绍七个非常有用的Python库&#xff0c;这些库各自有着独特…

js调用gpt3.5

参考链接&#xff1a;直接在前端调用 GPT-3 API 效果图&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>ChatGPT Web Example</title><style>body {font-family: "Helvetica Neue"…

TimeQuest时序路径详解

&#x1f4a1; 基于TimeQuest软件来查看时序报告和分析时序路径 分析最坏传输路径 根据[FPGA典型时序路径的分析可知&#xff0c;最坏传输路径对应的建立时间&#xff08;setup time&#xff09;余量最小。所以&#xff0c;查看最坏传输路径也就是查看建立时间余量最小的路径。…

【Linux】安装DHCP服务器

1、先检测网络是否通 get dhcp.txt rpm -qa //查看软件包 rpm -qa |grep dhcp //确定是否安装 yum install dhcp //进行安装 安装完成后 查询 rpm -ql dhcp 进行配置 cd /etc/dhcp 查看是否有遗留dhcpd.conf.rpmsave 删除该文件 cp /usr/share/doc/dhcp-4.1.1/dhcpd.conf.sampl…

ChatGPT能代替Oracle DBA吗?用Oracle OCP(1z0-083)的真题测试一下。

让我们来看看ChatGPT不能通过Oracle OCP的考试&#xff1f; 文章目录引言测试过程总结和分析关于博主&#xff0c;姚远&#xff1a;Oracle ACE&#xff08;Oracle和MySQL数据库方向&#xff09;。Oracle MAA 大师。华为云MVP。《MySQL 8.0运维与优化》的作者。拥有 Oracle 10g和…

mysql和mysql2模块的区别!!(nodejs中的模块)

mysql 和 mysql2 都是 Node.js 中常用的操作 MySQL 数据库的模块&#xff0c;它们的主要区别是在实现方式上略有不同。 mysql&#xff1a;是 Node.js 中比较早期的 MySQL 操作模块&#xff0c;该模块底层使用的是回调函数&#xff08;callback&#xff09;来实现异步操作。在处…