前言
文章
- 相关系列:《Java ~ Executor【目录】》(持续更新)
- 相关系列:《Java ~ Executor【源码】》(学习过程/多有漏误/仅作参考/不再更新)
- 相关系列:《Java ~ Executor【总结】》(学习总结/最新最准/持续更新)
- 相关系列:《Java ~ Executor【问题】》(学习解答/持续更新)
- 涉及内容:《Java ~ Executor ~ ExecutorService【总结】》
- 涉及内容:《Java ~ Executor ~ ThreadPoolExecutor【总结】》
一 Executor(执行器)接口源码及机制详解
接口
Executor(执行器)接口是执行器框架的最顶级接口,定义了执行器的概念,并明确了该框架的核心目标:将任务的递交与执行分离(并负责执行)。执行器接口的结构非常简单,只有一个execute(Runnable command)方法用于递交任务。正是因为该方法的存在,开发者无需再自我实现繁复的执行流程,而只需将任务创建并递交即可。任务的执行并不似想象中简单,它需要在性能和资源之间相互权衡,并要兼容实际场景的各项制约,还要满足预期设想的卓越效果,因此是一件费心费力的事情,特别是如果这件事情还要做很多次的话…任务的后续执行将彻底交由执行器负责,对开发者而言完全屏蔽,从而达到了“将任务的递交与执行分离(并负责执行)”的目的。因此可以说正是由于execute()方法的存在,执行器框架的作用才得以实现。
在实际开发中,我们推荐使用执行器代替显式创建线程的方式来执行任务(这是最常见的任务执行方式,一般是为了获得更高的性能),以获取更好的执行效果,案例如下:
Executor executor = new Executor() {...}};
executor.execute(new RunnableTask());
executor.execute(new RunnableTask());
执行器接口虽然在执行器框架中的拥有最高的地位,但由于其功能过于单一,因此在实际开发中往往会使用其子接口ExecutorService(执行器服务)代替。执行器服务接口是一个功能更加广泛的接口,在执行器接口的基础上引入了关闭、终止及结果等概念,更加贴合实际的使用需求,关于该接口的具体内容会在其专项文章中阐述,此处不再提及。
/**
* 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()}
* <p>
* TODO 这是一段用于演示的伪代码,大意是使用一个Executor对象来执行Runnable任务,而不是显式的创建一个个的线程来执行。
* <pre>
* Executor executor = <em>anExecutor</em>;
* executor.execute(new RunnableTask1());
* executor.execute(new RunnableTask2());
* ...
* </pre>
* <p>
* 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接口并不严格要求执行是异步的。在最简单的情况下,执行器可以立即在调用者的线程中运行提交的任务:(执行器
* 在定义上并不要求是异步的,其核心目标是为了复用已经存在的线程而避免显式的创建新线程)
*
* <pre> {@code
* class DirectExecutor implements Executor {
* public void execute(Runnable r) {
* // 直接使用当前线程对任务进行处理。
* r.run();
* }
* }}</pre>
* <p>
* More typically, tasks are executed in some thread other than the caller's thread. The executor below spawns a new thread for each
* task.
* 更典型的,任务在调用者线程之外的线程中执行。下面的执行器为了每个任务生成一个新线程。
*
* <pre> {@code
* class ThreadPerTaskExecutor implements Executor {
* public void execute(Runnable r) {
* // 新增一个新的线程执行。
* new Thread(r).start();
* }
* }}</pre>
* <p>
* 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.
* 许多执行器实现对任务调度的方式和时间施加了某种限制。下面的执行器将任务的提交序列化到第二个执行器,演示了复合执行器。
*
*
* <pre> {@code
* class SerialExecutor implements Executor {
* // 数组双端队列,用于保存可执行任务。
* final Queue<Runnable> tasks = new ArrayDeque<Runnable>();
* // 执行器。
* 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);
* }
* }
* }}</pre>
* <p>
* 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,所以除非真的任务数量很庞大或者难以把握,否则还是推荐手动的创建执行器)。
* <p>
* Memory consistency effects: Actions in a thread prior to submitting a {@code Runnable} object to an {@code Executor}
* <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* 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
* @Description: 名称:执行
* @Description: 作用:执行指定任务。
* @Description: 逻辑:~
*/
void execute(Runnable command);