Java ~ Executor
admin
2024-02-20 13:27:51
0

一 Executor(执行器)接口概述


 简介

    Executor(执行器)接口是执行框架最顶级的接口,定义了执行器的概念,并明确了该框架的核心目标:将任务的(创建)递交与执行分离(并负责执行)。Executor(执行器)接口的结构非常简单,其只有一个execute()方法,用于执行被递交来的任务。正是因为该方法的存在,开发者无需再自我实现繁复的执行流程(任务的执行并不似想象中简单,它需要在性能和资源之间相互权衡,并要兼容实际场景的各项制约,还要满足预期设想的卓越效果,因此是一件费心费力的事情,特别是如果这件事情还要做很多次的话…),而只需将任务创建并递交即可。任务的后续执行将彻底交由执行框架负责,对开发者而言完全屏蔽,从而达到了“将任务的(创建)递交与执行分离(并负责执行)”的目的。因此可以说正是由于execute()方法的存在,执行框架的作用才得以实现。

    在实际开发中,我们推荐使用Executor(执行器)接口对象来代替使用显式创建线程的方式(这是最常见的任务执行方式,一般是为了获得更高的性能)来获取更好的任务执行效果,案例如下:

Executor executor = new Executor() {...}};
executor.execute(new RunnableTask());
executor.execute(new RunnableTask());

 无关同步/异步

    Executor(执行器)接口并不严格(即推荐但不强制)要求任务的执行是异步的。很多资料在讲述执行框架时,都会直接将之称呼为“线程池”来阐述(强调)其多线程及异步的特性,但这是完全错误的。之所以会有很多人将两者画上等号是因为在Executor(执行器)接口的所有实现中ThreadPoolExecutor(线程池执行器)类是最贴合实际开发场景且往往是能最快最好的获得收益的存在,使的众多开发者忽视(甚至是不知道)了其它实现而独尊“线程池”(网上虽然找篇文章基本都是讲线程池的,其它的绝口不言)。但上文已经提及过,执行框架的核心目标是“将任务的(创建)递交与执行分离(并负责执行)”,而对于任务的执行方式,则由各项子类自行实现,所谓的异步也只是众多执行方式的其中之一罢了。作为执行框架的最顶级,Executor(执行器)接口没有在任何方面强制在其子类在实现中只能使用异步的方式。因此请注意不要把执行框架与异步强绑定,两者之间不存在必然关系。但话虽如此,采用异步却依旧最推荐的实现方案,因为这样往往能获得最高的效益。

    Executor(执行器)接口的同步实现方式案例如下:

public class DirectExecutor implements Executor {public void execute(Runnable r) {// 直接使用调用线程对任务进行处理。r.run();}}

 广泛实现

    Executor(执行器)接口虽然在执行框架中的拥有最高的地位,但由于其功能过于单一,因此在实际开发中往往会使用其子接口ExecutorService(执行器服务)代替。ExecutorService(执行器服务)是一个功能更加广泛的接口,在Executor(执行器)接口的基础上引入了关闭、终止及结果等概念,更加贴合实际的使用需求,关于该接口的具体内容会在其专项文章中阐述,此处不再提及。

二 Executor(执行器)接口源码及机制详解


 类

    Executor(执行器)接口是执行框架最顶级的接口,定义了执行器的概念,并明确了该框架的核心目标:将任务的(创建)递交与执行分离(并负责执行)。

/*** An object that executes submitted {@link Runnable} tasks. This interface provides a way of decoupling task submission from the* mechanics of how each task will be run, including details of thread use, scheduling, etc.  An {@code Executor} is normally used* instead of explicitly creating threads. For example, rather than invoking {@code new Thread(new(RunnableTask())).start()} for* each of a set of tasks, you might use:* 一个执行提交的Runnable任务的对象。该接口提供了一种将任务提交与每个任务如何运行的机制分离的方法,包括线程使用、调度* 等细节。通常使用Executor来代替显式创建线程。例如,对于每一组任务,你可以使用以下方法,而不是调用{@code new Thread(new(RunnableTask())).start()}* 

* TODO 这是一段用于演示的伪代码,大意是使用一个Executor对象来执行Runnable任务,而不是显式的创建一个个的线程来执行。*

* Executor executor = anExecutor;* executor.execute(new RunnableTask1());* executor.execute(new RunnableTask2());* ...* 
*

* However, the {@code Executor} interface does not strictly require that execution be asynchronous. In the simplest case, an executor* can run the submitted task immediately in the caller's thread:* 然而,Executor接口并不严格要求执行是异步的。在最简单的情况下,执行器可以立即在调用者的线程中运行提交的任务:(执行器* 在定义上并不要求是异步的,其核心目标是为了复用已经存在的线程而避免显式的创建新线程)**

 {@code* class DirectExecutor implements Executor {*     public void execute(Runnable r) {*         // 直接使用当前线程对任务进行处理。*         r.run();*     }* }}
*

* More typically, tasks are executed in some thread other than the caller's thread. The executor below spawns a new thread for each* task.* 更典型的,任务在调用者线程之外的线程中执行。下面的执行器为了每个任务生成一个新线程。**

 {@code* class ThreadPerTaskExecutor implements Executor {*     public void execute(Runnable r) {*         // 新增一个新的线程执行。*         new Thread(r).start();*     }* }}
*

* Many {@code Executor} implementations impose some sort of limitation on how and when tasks are scheduled. The executor below* serializes the submission of tasks to a second executor, illustrating a composite executor.* 许多执行器实现对任务调度的方式和时间施加了某种限制。下面的执行器将任务的提交序列化到第二个执行器,演示了复合执行器。***

 {@code* class SerialExecutor implements Executor {*     // 数组双端队列,用于保存可执行任务。*     final Queue tasks = new ArrayDeque();*     // 执行器。*     final Executor executor;*     // 活动。*     Runnable active;**     SerialExecutor(Executor executor) {*         // 传入一个可执行器。*         this.executor = executor;*     }**     public synchronized void execute(final Runnable r) {*         // 从队列中存入一个任务...但是为什么要用一个新的任务来承接旧的任务?*         tasks.offer(new Runnable() {*             public void run() {*                 try {*                     r.run();*                 } finally {*                     // 当任务具体被执行后,要继续执行下一个任务。*                     scheduleNext();*                 }*             }*         });*         // 如果没有正在执行的任务,则作为起点触发整体的执行,应该只有第一个任务入队时会进行触发。*         if (active == null) {*             scheduleNext();*         }*     }**     protected synchronized void scheduleNext() {*         // 从队列中获取任务并使用真正有执行能力的执行器进行执行。SerialExecutor只是起对任务的管理作用。*         if ((active = tasks.poll()) != null) {*             executor.execute(active);*         }*     }* }}
*

* The {@code Executor} implementations provided in this package implement {@link ExecutorService}, which is a more extensive interface.* The {@link ThreadPoolExecutor} class provides an extensible thread pool implementation. The {@link Executors} class provides* convenient factory methods for these Executors.* 这个包中提供的Executor实现实现了ExecutorService,这是一个更广泛的接口。ThreadPoolExecutor类提供了一个可扩展的线程池实现。* Executors类为这些executor提供了方便的工厂方法(不推荐使用Executors来创建执行器实例,因为创建的执行器很消耗资源,数量多的* 情况下容易造成OOM,所以除非真的任务数量很庞大或者难以把握,否则还是推荐手动的创建执行器)。*

* Memory consistency effects: Actions in a thread prior to submitting a {@code Runnable} object to an {@code Executor}* happen-before* its execution begins, perhaps in another thread.* 内存一致性的影响:在向Executor提交Runnable对象之前,线程中的操作发生在它的执行开始之前,可能是在另一个线程中。(这一段不* 是特别懂,关于内存一致性本身和操作系统有关,暂时不管)** @author Doug Lea* @Description: 执行器接口* @since 1.5*/ public interface Executor {... }

 方法

    void execute(Runnable command) —— 执行 —— 虽然方法的名称是执行,但本质却是递交,递交后的任务将在未来的某个时间执行。

/*** Executes the given command at some time in the future.  The command may execute in a new thread, in a pooled thread, or in the* calling thread, at the discretion of the {@code Executor} implementation.* 在未来的某个时间执行指定的命令。这个命令可能在一个新线程、一个池线程或者调用线程中执行,这取决于执行器的实现。** @param command the runnable task 可执行任务* @throws RejectedExecutionException if this task cannot be accepted for execution 如果这个任务无法被接受执行* @throws NullPointerException       if command is null 如果命令为null*/
void execute(Runnable command);

三 相关系列


  • 《Java ~ Executor》

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...