Java ~ Executor ~ AbstractExecutorService【总结】

news2024/11/20 15:18:55

前言


 文章

  • 相关系列:《Java ~ Executor【目录】》(持续更新)
  • 相关系列:《Java ~ Executor ~ AbstractExecutorService【源码】》(学习过程/多有漏误/仅作参考/不再更新)
  • 相关系列:《Java ~ Executor ~ AbstractExecutorService【总结】》(学习总结/最新最准/持续更新)
  • 相关系列:《Java ~ Executor ~ AbstractExecutorService【问题】》(学习解答/持续更新)
  • 涉及内容:《Java ~ Executor【总结】》
  • 涉及内容:《Java ~ Executor ~ ExecutorService【总结】》
  • 涉及内容:《Java ~ Executor ~ ExecutorCompletionService【总结】》
  • 涉及内容:《Java ~ Executor ~ Future【总结】》
  • 涉及内容:《Java ~ Executor ~ FutureTask【总结】》
  • 涉及内容:《Java ~ Executor ~ Callable【总结】》
  • 涉及内容:《Java ~ Thread ~ Runnable【总结】》

一 概述


 简介

    AbstractExecutorService(抽象执行器服务)抽象类是ExecutorService(执行器服务)接口的抽象实现类,其作用是对执行器服务接口的部分方法定义进行流程规划。所谓流程规划,可以理解为不完整实现。即整体运行流程虽然已经编码,但其中调用的部分方法却没有实现或没有正常实现。这部分方法可能是抽象方法,也可能是直接抛出不支持操作异常的破坏性实现,子类有权利/义务具体实现这些调用方法以使得整个运行流程变得完整可用。这种在父类中规划具体流程而将各个功能的实现细节交由子类负责的编码模式被称为“模板模式”,是常见设计模式中的一种。在Java的各个框架中基本以Abstract为名称开头的类都采用了该模式实现,例如Collection(集)框架的AbstractCollection(抽象集)抽象类、AbstractList(抽象列表)抽象类及AbstractSet(抽象集合)抽象类等。抽象模式的使用极大降低/减少了子类实现的难度/代码量,因为子类无需再去重复的关注/设计/实现那些通用的传递/连接/转换性质的中间代码,而可以集中精力专注在那些具体功能的实现上。不过虽说好处如此,但由于“模板模式”在整体流程的规划上需要兼容所有的子类,因此通常难以保证全局性能的优异性,即无法保证所有子类在各自实现理念上都能达到相对较高的性能。故而即使有时父类已经基于“模板模式”对某方法进行了默认的流程规划,但部分子类出于性能上的考量还是会选择重写该方法。

    抽象执行器服务抽象类规划了执行器服务接口定义的所有与任务递交相关方法的运行流程,该知识点的内容会在下文详述。

 模板模式结构图

在这里插入图片描述

二 流程规划


 递交

  • submit(Runnable task)

  • submit(Runnable task, T result)

  • submit(Callable<T> task)

    递交系方法的作用是向当前执行器递交可运行/可调用/任务,并返回可追踪/获取可运行/可调用/任务执行状态/结果的Future(未来)。

    抽象执行器服务抽象类将递交系方法的流程规划划分为“可运行/可调用/任务封装”及“可运行/可调用/任务执行”两个功能。所谓“可运行/可调用/任务封装”功能即是将传入的可运行/可调用/任务封装为未来以用于后续执行及方法返回,该功能由抽象执行器服务抽象类自定义实现的newTaskFor(新任务)系方法完成。新任务系方法的实现非常简单,其会直接将可运行/可调用/任务作为构造方法的参数来创建默认的FutureTask(未来任务)。具体源码如下:

  • protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) —— 新任务 —— 为指定的可运行/任务和用于承接执行结果的变量创建可运行未来。
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}
  • protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) —— 新任务 —— 为指定的可调用/任务创建可运行未来 。
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

    未来任务类是抽象执行器服务抽象类默认规定的未来接口实现类,即如果没有重写规则,可运行/可调用/任务会被默认封装为未来任务。但现实是抽象执行器服务抽象类子类有时需要与未来接口实现类进行专项绑定,即针对不同实现思想的抽象执行器服务抽象类子类,可能需要为之定制专项未来接口实现类以封装可运行/可调用/任务。因此为了支持自由化选择,抽象执行器服务抽象类将新任务系方法设计为protected访问权限,从而令抽象执行器服务抽象类子类可以重写该系方法以实现对未来接口实现类的定制。这种提供了功能默认实现但又允许其自定义重写的做法也是“模板模式”继不实现(抽象方法)及破坏性实现之外的另一种使用方式。而更多时候,对于默认实现功能的方法通常都会被修饰final关键字,即不允许重写。

     “可运行/可调用/任务执行”功能由execute(Runnable command)方法负责执行。抽象执行器服务抽象类并没有对execute(Runnable command)方法提供默认实现,即该方法为无实现的抽象方法,抽象执行器服务抽象类子类需要重写该方法以令递交系方法可用,并可通过其对可运行/可调用/任务的执行方式进行自定义实现。

在这里插入图片描述

 调用任意

  • invokeAny(Collection<? extends Callable<T>> tasks)

  • invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

    调用任意系方法的作用是向当前执行器递交指定可调用/任务集,并返回其中一个已完成可调用/任务的执行结果。该方法会有限/无限地等待递交的可调用/任务集中的一个执行完成(异常/取消不计算在内),无论最终是否超时,其余未结束(等待中/执行中)的可调用/任务都将被取消。而如果在指定等待时间内所有可调用/任务都执行失败(异常/取消)则会抛出执行异常;而超时则会抛出超时异常。由于Java是响应式中断,因此取消的具体表现为等待中的可调用/任务不再执行,而执行中可调用/任务的执行线程会被中断,但具体能否取消成功则由任务能否响应中断及执行时机决定。

    抽象执行器服务抽象类对调用任意系方法的流程规划完成度非常之高,已经接近完全实现的地步。execute(Runnable command)方法是调用任意系方法仅有还需要子类实现的功能,其流程规划大致如下:

  • 创建未来集,用于容纳由指定可调用/任务集中的可调用/任务封装而来的未来;
  • 循环指定可调用/任务集,依次将可调用/任务递交至ExecutorCompletionService(执行器完成服务)并将返回的未来加入未来集中。每次递交之前方法都会尝试从执行器完成服务中获取结束(完成/异常/取消)的未来,如果成功获取,则会暂时中止递交并判断未来是否完成。是则准备返回结果;否则说明该结束(完成/异常/取消)未来为异常/取消,需要继续向执行器完成服务递交可调用/任务;
  • 所有可调用/任务递交后,方法会有限/无限地等待执行器完成服务直至获取到完成未来的结果或因超时而抛出超时异常为止。此外还有一种特殊情况,即所有未来都异常/取消。如此情况下会将最后未来的异常(执行/取消/运行时异常)记录下来并封装为执行时异常抛出。如果不存在异常记录,说明是程序没有覆盖到的其它异常,此时会抛出一个默认的执行异常。
  • 无论方法最终是成功获取到完成未来的结果、还是抛出超时/执行异常,返回前都要先对未来集中的所有未来调用cancel(boolean mayInterruptIfRunning)方法,目的是取消未来集中尚未结束(完成/异常/取消)的未来。但这么做会对未来集中结束(完成/异常/取消)的未来执行无意义操作,因为结束(完成/异常/取消)的未来是无法被取消的。

    执行调用任意系方法前要判断执行器中是否存在未执行任务。由于方法会等待至返回其中一个已完成可调用/任务的执行结果,因此如果执行器中存在大量的任务未执行,则可能导致调用任意系方法长时间的等待。并且与之前的任务并发执行也会影响调用任意系方法的执行效率。因此在执行调用任意系方法前要判断执行器中是否存在未执行任务。

 调用所有

  • invokeAll(Collection<? extends Callable<T>> tasks)

  • invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

    调用所有系方法的作用是向当前执行器递交指定可调用/任务集,并返回追踪/获取可调用/任务执行状态/结果/异常的未来集。方法会有限/无限地等待递交的可调用/任务集执行结束(完成/异常/取消),如果超时则会取消其中未结束(等待中/执行中)的可调用/任务,因此返回未来集中所有未来的isDone()方法调用结果都为true。由于Java是响应式中断,因此取消的具体表现为等待中的可调用/任务不再执行,而执行中可调用/任务的执行线程会被中断,但具体能否取消成功则由任务能否响应中断及执行时机决定。

    与调用任意系方法相同,调用所有系方法同样也只依赖子类实现execute(Runnable command)方法。虽说没有采用共同的底层方法,但两者的逻辑大致相同,其流程规划如下:

  • 创建未来集,用于容纳由指定可调用/任务集中所有可调用/任务封装而来的未来;
  • 循环指定可调用/任务集,依次将可调用/任务封装为未来并加入未来集中,随后执行未来。invokeAll(Collection<? extends Callable> tasks)方法会在一次循环同步完成封装与执行两步操作;而invokeAll(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)方法则会分两次循环分别进行,即一次循环封装未来,二次循环执行未来。后者分为两次进行的原因是因为可调用/任务的执行逻辑由子类负责实现,同步执行会难以保证在指定等待时间内将所有可调用/任务封装为未来并加入未来集,导致未来集不完整,所以才会设计专属封装的循环来避免。在流程规划上,由于单纯的封装是非常简单快捷的操作,因此程序默认忽略了该过程的时间消耗;
  • 所有未来执行后,方法会在限/无限的时间里循环未来集,并依次调用get()/get(long timeout, TimeUnit unit)方法等待未来执行结束(完成/异常/取消)。由于get()/get(long timeout, TimeUnit unit)方法是等待方法,因此只有当前未来结束(完成/异常/取消)后才可以对下个未来进行调用;
  • 如果成功等待到所有的未来执行结束(完成/异常/取消),则方法会直接返回未来集;而如果get(long timeout, TimeUnit unit)方法抛出超时异常导致不再等待后续未来,则方法在返回未来集前要先对未来集中的所有未来调用cancel(boolean mayInterruptIfRunning)方法,目的是取消未来集中尚未结束(完成/异常/取消)的未来。但这么做会对未来集中结束(完成/异常/取消)的未来执行无意义操作,因为结束(完成/异常/取消)的未来是无法被取消的。

    执行调用所有系方法前要判断执行器中是否存在未执行任务。由于方法会等待至递交的可调用/任务集执行结束(完成/异常/取消),因此如果执行器中存在大量的任务未执行,则可能导致调用所有系方法长时间的等待。并且与之前的任务并发执行也会影响调用所有系方法的执行效率。因此在执行调用所有系方法前要判断执行器中是否存在未执行任务。

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

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

相关文章

《痞子衡嵌入式半月刊》 第 78 期

痞子衡嵌入式半月刊&#xff1a; 第 78 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻&#xff0c;农历年分二十四节气&#xff0c;希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly)&#xff0c;欢迎提交 issue&#xff0c…

2023双态IT北京用户大会回顾(三) | 银行分布式核心智能运维体系思考

​文末附有本场专题演讲视频 2023第五届双态IT北京用户大会&#xff0c;擎创科技专场演讲回顾之三&#xff1a;《银行分布式核心智能运维体系思考》&#xff0c;演讲嘉宾&#xff1a;擎创科技生态合作部总经理 冯陈湧 一、前言&#xff1a;分布式云原生快速发展催生更高的业务要…

解密Docker容器网络

一个Linux容器能看见的“网络栈”&#xff0c;被隔离在它自己的Network Namespace中。 1 “网络栈”的内容 网卡&#xff08;Network Interface&#xff09;回环设备&#xff08;Loopback Device&#xff09;路由表&#xff08;Routing Table&#xff09;iptables规则 对于一…

【PCB专题】Allegro如何设置电源电压属性,将飞线隐藏?

在PCB设计过程中,布局完成之后的布线的顺序一般是先走信号线,然后进行电源的处理、分割。因为电源往往在整个板子上是都有的,所以电源的飞线是非常多,在布线时特别影响其他信号线的布线,界面看着比较杂乱。 如下所示GND和1.8V都存在各种飞线,比较杂乱。 Allegro中有设置…

随想012:断言

C 标准库提供了名为 assert 的断言宏&#xff1b; C# 语言提供了名为 Debug.Assert 的断言方法&#xff1b; Java 语言提供名为 assert 的断言关键字。 主流编程语言不约而同的在语言层面上提供了 断言 机制。 David R. Jamson&#xff0c;编译器 Icc 的开发者之一&#xf…

掌握IO流这一篇就够了

IO流&#xff08;几种常见的流&#xff09; IO流概述IO的分类顶级父类 字节流、字符流字节输出流OutputStream字节输入流InputStreamFileOutputStream类FileOutputStream**写出字节数据** FileInputStream类FileInputStream读取字节数据复制图片 字符流字符输入流Reader字符输出…

主动式和被动式电容笔哪个好用?苹果平板平替笔排行

被动式电容笔与主动式电容笔最大的不同在于主动式电容笔具有更广泛的应用领域&#xff0c;可以与不同种类的电容屏幕进行匹配。随着人们对其了解的不断深入&#xff0c;其应用也日益广泛。除此之外&#xff0c;平替电容笔的技术&#xff0c;也在不断的改进和提高&#xff0c;逐…

ResizeKit.NET 自动更改所有控件和字体大小 -Crack Version

ResizeKit2.NET ---Added support for Microsoft .NET 7.0. 使您的应用程序大小和分辨率独立。 ResizeKit.NET 自动更改所有控件和字体的大小&#xff0c;以便它们可以显示在任何大小的表单上。提供完全控制来自定义调整大小过程。即使用户在运行应用程序时切换表单的大小&…

ReentrantLock源码

介绍 ReentrantLock是Java中的一种可重入锁&#xff0c;也称为重入锁。是一种递归无阻塞的同步机制。它可以等同于 synchronized 的使用&#xff0c;但是 ReentrantLock 提供了比 synchronized 更强大、灵活的锁机制&#xff0c;可以减少死锁发生的概率。 ReentrantLock实现了…

Spring学习笔记---下篇

文章目录 Spring下篇1、代理模式1.1、静态代理1.2、加深理解1.3、动态代理 2、AOP2.1、什么是AOP2.2 、Aop在Spring中的作用2.3、在Spring中使用Aop 3、整合MyBatis3.1、[mybatis-spring介绍](https://mybatis.org/spring/zh/getting-started.html)3.2、整合步骤 4、事务4.1、事…

Linux基础工具|文本编辑器Vim的使用

0.前言 您好这里是limou3434的个人博客&#xff0c;感兴趣可以看看我的其他内容。 本次我给您带来的是Linux下Vim文本编辑器的使用&#xff0c;关于vim&#xff0c;您只需要知道一些常用的指令和操作即可&#xff0c;快速上手的秘诀是实践&#xff0c;并且是多次实践。 1.Vi…

12 MFC常用控件(一)

文章目录 button 按钮设置默认按钮按下回车后会响应禁用开启禁用设置隐藏设置显示设置图片设置Icon设置光标 Cbutton 类创建按钮创建消息单选按钮多选按钮 编辑框组合框下拉框操作 CListBox插入数据获取当前选中 CListCtrl插入数据设置表头修改删除 button 按钮 设置默认按钮按…

将 YAPF 设置为默认的 Python 代码格式化工具 (VS Code, PyCharm)

yapf 是一个 python 代码格式化工具, 和 black, autopep8, pycharm 自带的格式化功能相同用途. 使用 yapf 作为我的默认格式化工具, 出于以下考虑: 我和团队使用多种 ide, 而 pycharm 自带的格式化功能在其他 ide 上没法用. 所以我需要一个通用的格式化方案来保持代码风格的一…

Unity | HDRP高清渲染管线学习笔记:Rendering Debugger窗口

HDRP给我们提供了一套完整的可视化Debug工具&#xff0c;集成在Rendering Debugger窗口。通过顶部菜单Window→Analysis→Rendering Debugger可以打开窗口。Rendering Debugger窗口不仅仅可以在编辑模式下使用&#xff0c;也可以在真机上运行时使用。&#xff08;要在真机上运行…

数据结构--栈(Stack)的基本概念

数据结构–栈(Stack)的基本概念 线性表是具有相同数据类型的n ( n ≥ 0 n\ge0 n≥0&#xff09;个数据元素的有限序列&#xff0c;其中n为表长&#xff0c;当n 0时线性表是一个空表。若用L命名线性表&#xff0c;则其一般表示为: L ( a 1 , a 2 . . . , a i , a i 1 , . . …

JavaScript 手写代码 第七期(重写数组方法三) 用于遍历的方法

文章目录 1. 为什么要手写代码&#xff1f;2. 手写代码2.1 forEach2.1.1 基本使用2.1.2 手写实现 2.2 map2.2.1 基本使用2.2.2 手写实现 2.3 filter2.3.1 基本使用2.3.2 手写实现 2.4 every2.4.1 基本使用2.4.2 手写实现 2.5 some2.5.1 基本使用2.5.2 手写实现 2.6 reduce2.6.1…

大学实训报告范文6篇

大学实训报告范文篇一&#xff1a;js实训报告 一、简介&#xff1a; Web标准并不是一个单一的标准&#xff0c;而是一个系列的标准的集合。Web标准中具有代表性的几种语言有&#xff1a;_ML可扩展标记语言、_HTML可扩展超文本标记语言、CSS层叠样式表、DOM文档对象模型、Java…

助你丝滑过度到 Vue3 常用的组合式API ②④

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; VUE3~TS &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f449;…

HOT25-环形链表

leetcode原题链接&#xff1a;环形链表 题目描述 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数…

数据库分库分表(上)

数据库分库分表 1&#xff0c;概念 分库分表是一种数据库架构设计的方法&#xff0c;用于解决大规模数据存储和查询的性能问题。它将一个大型数据库拆分成多个小型数据库&#xff0c;每个数据库负责存储一部分数据&#xff0c;从而提高数据的读写效率和并发处理能力。 分库分…