Executor框架详解

Stella981
• 阅读 530

任务是一组逻辑工作单元,线程是使任务异步执行的机制。下面分析两种通过创建线程来执行任务的策略。

1 将所有任务放在单个任务中串行执行;

2 为每个任务创建单独的线程来执行

实际上两种方式都存在一些严重的缺陷。串行执行的问题在于其糟糕的响应和吞吐量;而为每个任务单独创建线程的问题在于资源管理的复杂性,容易造成资源的浪费和过度消耗,影响系统的稳定性。为了提高任务执行的效率和系统的吞吐量,合理分配和利用资源,线程池应运而生。

什么是线程池?

线程池是指管理一组同构工作线程的资源池。线程池与工作队列密切相关的,其中工作队列中保存了所有等待执行的任务。工作者线程的任务是从工作队列中获取一个任务,执行任务,然后回线程池并等待下一个任务。

Executor框架

Executor框架是java5引入用于执行Runnable任务的接口,但并不是一个简单的接口,Executor为实现灵活且强大的异步任务执行框架提供了基础,该框架支持多种不同类型的任务执行策略。它提供了一种标准的方法将任务的提交过程与执行过程解耦开来,并用Runnable来表示任务。Executor的实现还提供了对生命周期的支持,以及统计信息收集、应用程序管理和性能监视等机制。

Executor基于生产者-消费者模式,提交任务的操作相当于生产者,执行任务的操作则相当于消费者。

Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。类图关系如下:

Executor框架详解

Executor接口定义了一个方法execute(Runnable command),该方法接收一个Runable实例,它用来执行一个任务。

void execute(Runnable command);

ExecutorService

ExecutorService接口继承自Executor接口,它提供了更丰富的方法,比如,关闭线程池,以及为跟踪一个或多个异步任务执行状况而生成 Future 的方法。下面详细介绍ExecutorService接口中各方法的功能。

void shutdown();

 shutdown()方法来平滑地关闭 ExecutorService,调用该方法后,ExecutorService将停止接受新任务,但会执行完已经提交的任务(一类是已经在执行的,另一类是还没有开始执行的)。当所有已经提交的任务执行完毕后关闭ExecutorService。

List<Runnable> shutdownNow();

调用该方法,则将尝试停止所有正在执行的任务,暂停处理正在等待的任务,并返回正在等待任务的列表。

注意:该方法无法保证能够停止正在执行的任务,通常是通过Thread.interrupt()向任务发送一个中断信号,如果任务本身并不响应中断则无法停止任务。

boolean isShutdown();

ExecutorService是否已经关闭。

boolean isTerminated();

关闭ExecutorService后所有任务都已完成则返回true,除非调用了shutdown 或 shutdownNow,否则 isTerminated 永不为 true。

boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

shutdown请求发生超时或当前线程发生中断,无论先发生哪一个,都将导致阻塞直到所有任务执行完成。

<T> Future<T> submit(Callable<T> task);

提交一个具Callable任务用于执行,返回一个表示任务执行结果的Future。该 Future 的 get 方法在成功完成时将会返回该任务的结果。

<T> Future<T> submit(Runnable task, T result);

提交一个 Runnable 任务用于执行,并返回一个表示该任务执行结果的 Future。该 Future 的 get 方法在成功完成时将会返回给定的结果。

Future<?> submit(Runnable task);

提交一个 Runnable 任务用于执行,并返回一个表示该任务执行结果的 Future。该 Future 的 get 方法在_成功_完成时将会返回null。

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;

执行给定的任务列表,当所有任务完成时,返回保持任务状态和结果的 Future 列表。返回列表的所有元素的Future.isDone为 true。

注意:可以正常地或通过抛出异常来终止_已完成_ 任务。如果正在进行此操作时修改了给定的 collection,则此方法的结果是不确定的。

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)
        throws InterruptedException;

执行给定的任务列表,当所有任务完成或达到超时时限,返回保持任务状态和结果的 Future 列表。返回列表的所有元素的Future.isDone为 true。一旦返回,取消没有完成的任务。(超时返回)

注意:可以正常地或通过抛出异常来终止_已完成_ 任务。如果正在进行此操作时修改了给定的 collection,则此方法的结果是不确定的。

<T> T invokeAny(Collection<? extends Callable<T>> tasks) 
               throws InterruptedException, ExecutionException;

执行给定的任务列表,如果某个任务已成功完成(未抛出异常),则返回其结果。一旦正常或异常返回后,则取消尚未完成的任务。

注意:如果正在进行此操作时修改了给定的 collection,则此方法的结果是不确定的。

<T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

执行给定的任务列表,如果达到超时时限或某个任务已成功完成(未抛出异常),则返回其结果。一旦正常或异常返回后,则取消尚未完成的任务。

注意:如果正在进行此操作时修改了给定的 collection,则此方法的结果是不确定的。

综上,ExecutorService的生命周期包括三种状态:运行、关闭、终止。创建后便进入运行状态,当调用了shutdown()方法时,便进入关闭状态,此时意味着ExecutorService不再接受新的任务,当有已经提交了的任务执行完后,便到达终止状态。如果不调用shutdown()方法,ExecutorService会一直处在运行状态,不断接收新的任务,执行新的任务。此外,ExecutorService还提供了大量任务异步执行的API,进一步扩展了Executor执行异步任务的能力。接下来介绍Executor是如何支持调度任务执行的。

ScheduledExecutorService

ScheduledExecutorService主要用于实现给定的延迟后执行或定时执行指定的任务,也就是常说的调度任务。其所有 schedule 方法都接受_相对_ 延迟和周期作为参数,而不是绝对的时间或日期。主要提供了以下几个重要方法:

 public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);

给定延迟执行一次指定的任务command,执行完成后ScheduledFuture返回null

public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);

给定延迟执行一次指定的任务callable,并返回保存任务执行结果的ScheduledFuture。

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

按指定频率周期性执行任务,任务在initialDelay后首次执行,且任务在(initialDelay+=period)后周期性地执行下去,只能通过执行程序的取消或终止方法来终止该任务。

注意:如果任务的任何一次执行遇到异常,将不再执行;如果任务的任何一次执行花费比其周期更长的时间,则将推迟后续执行,但不会同时执行。

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

按指定频率间隔周期性地执行任务,任务在initialDelay后首次执行,后续执行将在当前执行终止后间隔delay开始执行,只能通过执行程序的取消或终止方法来终止该任务。

注意:如果任务的任何一次执行遇到异常,将不再执行。

CompletionService

CompletionService是将生产新的异步任务与消费已完成任务的结果分离开来的服务,生产者负责 submit 要执行的任务,消费者负责take已完成的任务,并按照完成这些任务的顺序处理它们的结果。

CompletionService需要一个单独Executor来实际执行任务,其内部只负责管理一个完成队列。其主要操作说明:

Future<V> poll();

获取并移除表示下一个已完成任务的 Future,如果不存在这样的任务,则返回 null。获取并移除表示下一个已完成任务的 Future,如果不存在这样的任务,则返回 null。

Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;

获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则将等待指定的时间。

Future<V> take() throws InterruptedException;

获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则等待。

 Future<V> submit(Callable<V> task);

提交要执行的值返回任务,并返回表示挂起的任务结果的 Future。在完成时,可能会提取或轮询此任务。

Future<V> submit(Runnable task, V result);

提交要执行的 Runnable 任务,并返回一个表示任务完成的 Future,可以提取或轮询此任务。

Future

Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,计算完成前可根据需要阻塞此方法。可调用cancel方法取消计算,计算完成则无法取消。

若想通过Future实现计算的可取消功能,但无需提供返回结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。

部分方法说明:

boolean cancel(boolean mayInterruptIfRunning);

尝试取消任务的执行,如果任务已完成、或已取消,或者由于某些其他原因而无法取消,返回false。当调用 cancel 时,如果调用成功,而此任务尚未启动,则任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。

此方法返回后,后续调用isDone()方法将一直返回true;如果此方法返回true,后续调用isCancelled()将一直返回true。

V get() throws InterruptedException, ExecutionException;

等待计算完成,然后获取其结果。

RunnableFuture

作为Runnable任务的Future,成功执行 run 方法可以完成 Future 并允许访问其结果。

void run();

如果任务未被取消,将此 Future 设置为计算的结果。

欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/873089

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
2年前
java 面试知识点笔记(十三)多线程与并发
java线程池,利用Exceutors创建不同的线程池满足不同场景需求:1.newSingleThreadExecutor() 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。2.
Stella981 Stella981
2年前
Executor线程池
线程池为线程生命周期的开销和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。_0_|_1_线程实现方式Thread、Runnable、Callable//实现Runnable接口的类将被Thread执行,表示一个基本任务p
Wesley13 Wesley13
2年前
Java 并发编程:任务执行器 Executor 接口
任务执行器(Executor)是一个接口,位于java.util.concurrent包下,它的作用主要是为我们提供任务与执行机制(包括线程使用和调度细节)之间的解耦。比如我们定义了一个任务,我们是通过线程池来执行该任务,还是直接创线程来执行该任务呢?通过Executor就能为任务提供不同的执行机制。执行器的实现方式各种各样,常见的包括同步执行器、一对一执行
Stella981 Stella981
2年前
Executor框架
任务是一组逻辑工作单元,而线程则是使任务异步执行的机制。线程池简化了线程的管理工作,并且java.util.concurrent提供了一种灵活的线程池实现作为Executor框架的一部分。在Java类库中,任务执行的主要抽象不是Thread,而是Executor,如下所示:publicinterfaceExecutor{void
Wesley13 Wesley13
2年前
Java 多线程,线程池,
1\.创建线程池的方法之三://对于每个任务,如果有空闲的线程可用,立即让他执行任务,//没有空闲的线程则创建一个线程。ExecutorServicepoolExecutors.newCachedThreadPool();//固定大小的线程池,任务数空闲线程数,得不到服务的任务
Wesley13 Wesley13
2年前
Java多线程之任务执行
Java多线程之任务执行一、在线程中执行任务1.串行的执行任务在应用程序中可以通过多种策略来调度任务,而其中的策略能够更好的利用潜在的并发性。_最简单的策略就是在单个线程中串行的执行各项任务。_public class SingleThreadWebServer {
Wesley13 Wesley13
2年前
Java变成思想
Executor:线程池CatchedThreadPool:创建与所需数量相同的线程,在回收旧线程是停止创建新县城。FixedThreadPool:创建一定数量的线程,所有任务公用这些线程。SingleThreadPool:线程数量为1的FixedThreadPool,并且执行有序。如果需要得到线程返回值,要实现Callbale接口
Wesley13 Wesley13
2年前
Java中的线程池
java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理使用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺
京东云开发者 京东云开发者
4个月前
ThreadPoolExecutor线程池内部处理浅析 | 京东物流技术团队
我们知道如果程序中并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束时,会因为频繁创建线程而大大降低系统的效率,因此出现了线程池的使用方式,它可以提前创建好线程来执行任务。本文主要通过java的ThreadPoolExecutor来查看线程池
小万哥 小万哥
1个月前
深入理解 Java 多线程、Lambda 表达式及线程安全最佳实践
Java线程线程使程序能够通过同时执行多个任务而更有效地运行。线程可用于在不中断主程序的情况下在后台执行复杂的任务。创建线程有两种创建线程的方式。1.扩展Thread类可以通过扩展Thread类并覆盖其run()方法来创建线程:javapublicclas