1.概述

ExecutorService是JDK提供的框架,它简化了异步模式下的任务执行。一般来说,ExecutorService会自动提供一个线程池和API,用于为其分配任务。

2.实例化ExecutorService

2.1 Executors类的工厂方法

打造ExecutorService最简单的方法是使用的的工厂方法之一Executors类。

例如,以下代码行将创建一个包含10个线程的线程池:

ExecutorService executor = Executors.newFixedThreadPool(10);

还有其他几种工厂方法可以创建满足特定用例的预定义ExecutorService。要找到满足您需求的最佳方法,请参阅Oracle官方文档

2.2 直接创建ExecutorService

因为ExecutorService是一个接口,所以可以使用其任何实现的实例。在java.util.concurrent中有几种实现可供选择包中或者您可以创建自己的实现。

例如,ThreadPoolExecutor类有一些构造函数,可用于配置执行程序服务及其内部池。

ExecutorService executorService = new ThreadPoolExecutor(1,1, 0L,TimeUnit.MILLISECONDS,  new LinkedBlockingQueue<Runnable>());

您可能会注意到上面的代码与工厂方法newSingleThreadExecutor()源代码非常相似对于大多数情况,不需要详细的手动配置。

3.将任务分配给ExecutorService

ExecutorService可以执行RunnableCallable任务。为了简化本文,将使用两个原始任务。请注意,此处使用lambda表达式而不是匿名内部类:

Runnable runnableTask = () -> {
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}; Callable<String> callableTask = () -> { TimeUnit.MILLISECONDS.sleep(300); return "Task's execution"; }; List<Callable<String>> callableTasks = new ArrayList<>(); callableTasks.add(callableTask); callableTasks.add(callableTask); callableTasks.add(callableTask);

可以使用多种方法将任务分配给ExecutorService,包括从Executor接口继承的execute(),以及submit()invokeAny(),invokeAll()。

execute()方法是void的,而且不能获得任务的执行结果或检查任务的状态(是否运行或执行)。

executorService.execute(runnableTask);

submit()将一个CallableRunnable任务提交给ExecutorService并返回Future类型的结果。

Future<String> future = executorService.submit(callableTask);

invokeAny()将一组任务分配给ExecutorService,使每个任务执行,并返回成功执行一个任务的结果(如果成功执行)。

String result = executorService.invokeAny(callableTasks);

invokeAll()将一组任务分配给ExecutorService,使每个任务执行,并以Future类型的对象列表的形式返回所有任务执行的结果。

List<Future<String>> futures = executorService.invokeAll(callableTasks);

现在,在继续之前,还必须讨论另外两件事:关闭ExecutorService并处理Future返回类型。

4.关闭ExecutorService

一般来说,ExecutorService当没有要处理的任务时,不会自动销毁。它会活着并等待新的工作要做。

在某些情况下,这非常有用; 例如,如果应用程序需要处理不规则出现的任务,或者在编译时不知道这些任务的数量。

另一方面,应用程序可以到达它的终点,但它不会被停止,因为等待的ExecutorService将导致JVM继续运行。

要正确关闭ExecutorService,我们有shutdown()shutdownNow() API。

shutdown() 方法不会导致立即销毁ExecutorService。它将使ExecutorService停止接受新任务,并在所有正在运行的线程完成当前工作后关闭。

executorService.shutdown();

shutdownNow()方法试图立即摧毁ExecutorService,但是它并不能保证所有正在运行的线程将同时停止。此方法返回等待处理的任务列表。由开发人员决定如何处理这些任务。

List<Runnable> notExecutedTasks = executorService.shutDownNow();

关闭ExecutorServiceOracle推荐)的一个好方法是使用这两种方法结合awaitTermination()方法。使用这种方法,ExecutorService将首先停止执行新任务,等待指定的时间段完成所有任务。如果该时间到期,则立即停止执行:


executorService.shutdown(); try { if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); }

5. *Future *接口

submit()的invokeAll()方法返回一个对象或类型的对象的集合*Future *,这使我们能够得到一个任务的执行结果或检查任务的状态(是否运行或执行)。

Future 接口提供了一个特殊的阻塞方法get() ,它返回的实际结果,Callable任务的执行或无效的情况下Runnable任务。在任务仍在运行时调用get()方法将导致执行被阻塞,直到任务正确执行并且结果可用。


Future<String> future = executorService.submit(callableTask); String result = null; try { result = future.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }

由于get()方法导致非常长的阻塞,应用程序的性能会降低。如果结果数据不重要,可以通过使用超时来避免这样的问题:

String result = future.get(200, TimeUnit.MILLISECONDS);

如果执行周期长于指定的时间(在本例中为200毫秒),则将抛出TimeoutException

isDone()方法可用于检查所分配的任务已经处理或没有。

Future 接口还提供了任务执行的取消方法cancel(),以及检查取消状态的isCancelled()方法:

boolean canceled = future.cancel(true);

boolean isCancelled = future.isCancelled();

6. ScheduledExecutorService接口

ScheduledExecutorService的运行一些预定义的延迟和/或定期后任务。再一次,实例化ScheduledExecutorService的最佳方法是使用Executors类的工厂方法。

对于本节,将使用具有一个线程的ScheduledExecutorService

ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

要在固定延迟之后安排单个任务的执行,请使用ScheduledExecutorServicescheduled()方法。有两个scheduled()方法允许您执行RunnableCallable任务:

Future<String> resultFuture = executorService.schedule(callableTask, 1, TimeUnit.SECONDS);

scheduleAtFixedRate()方法允许一个固定的延迟之后定期执行的任务。上面的代码在执行callableTask之前延迟了一秒钟。

下面的代码块将在100毫秒的初始延迟后执行任务,之后,它将每450毫秒执行相同的任务。如果处理器需要更多时间来执行分配的任务而不是scheduleAtFixedRate()方法的period参数,则ScheduledExecutorService将等到当前任务完成后才开始下一个:

Future<String> resultFuture = service.scheduleAtFixedRate(runnableTask, 100, 450, TimeUnit.MILLISECONDS);

如果必须在任务的迭代之间具有固定长度的延迟,则应使用scheduleWithFixedDelay()。例如,以下代码将保证当前执行结束与另一个执行结束之间的暂停时间为150毫秒。

service.scheduleWithFixedDelay(task, 100, 150,TimeUnit.MILLISECONDS);

根据scheduleAtFixedRate()scheduleWithFixedDelay()方法契约,任务的句点执行将在ExecutorService终止时结束,或者在任务执行期间抛出异常

7. ExecutorService与Fork / Join

在Java 7发布之后,许多开发人员决定将ExecutorService框架替换为fork / join框架。然而,这并不总是正确的决定。尽管使用简单且与fork / join相关的频繁性能提升,但开发人员对并发执行的控制量也有所减少。

ExecutorService使开发人员能够控制生成的线程数以及应由不同线程执行的任务粒度。ExecutorService的最佳用例是处理独立任务,例如根据“一个任务的一个线程”方案的事务或请求。

相比之下,根据Oracle的文档,fork / join旨在加速工作,可以递归地分成更小的部分。

8.结论

尽管ExecutorService相对简单,但仍有一些常见的陷阱。让我们总结一下:

保持未使用的ExecutorService处于活动状态: 本文第4节中有关于如何关闭ExecutorService的详细说明;

使用固定长度线程池时错误的线程池容量:确定应用程序有效执行任务所需的线程数非常重要。一个太大的线程池只会产生不必要的开销,只是为了创建大多数将处于等待模式的线程。由于队列中任务的等待时间很长,因此太少可以使应用程序看起来没有响应;

任务取消后调用Future的get()方法: 尝试获取已取消任务的结果将触发CancellationException。

使用Future的get()方法意外地阻塞: 应该使用超时来避免意外的等待

image

原文地址:https://blog.csdn.net/Growing_stu/article/details/84328723

AbstractExecutorService的submit方法概要介绍的更多相关文章

  1. JMeter学习-005-JMeter 主要组件概要介绍及执行顺序

    本文将对 JMeter 主要组件(主要涉及 Threads(Users).Test Fragment.逻辑控制器.配置元件.定时器.前置处理器.Sampler.后置处理器.断言.监听器 十大组件)进行 ...

  2. 13.ThreadPoolExecutor线程池之submit方法

    jdk1.7.0_79  在上一篇<ThreadPoolExecutor线程池原理及其execute方法>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法 ...

  3. 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)

    在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...

  4. jquery 通过submit()方法 提交表单示例

    jquery 通过submit()方法 提交表单示例: 本示例:以用户注册作为例子.使用jquery中的submit()方法实现表单提交. 注:本示例仅提供了对表单的验证,本例只用选用了三个字段作为测 ...

  5. KEGG数据库的使用方法与介绍

    KEGG数据库的使用方法与介绍 KEGG的数据 KEGG中的pathway是根据相关知识手绘的,这里的手绘的意思可能是指人工以特定的语言格式来确定通路各组件的联系:基因组信息主要是从NCBI等数据库中 ...

  6. Ext.Ajax.request()方法和FormPanel.getForm().submit()方法,都返回success()方法的差异

    我还是不发表到博客园首页吧,要不然还是要被取消,>_< 还是言归正传吧,关于Ext.Ajax.request()方法和FormPanel.getForm().submit()方法返回suc ...

  7. js和jq使用submit方法无法提交表单

    昨天,在做一个表单异步提交内容的时候,遇到很奇怪的问题,submit()方法无法进行提交,每次提交都是把 当前给刷新了,网络抓包发现,每次都是 get方式去获取 当前页面,完全没有post 请求,想着 ...

  8. 使用Memcache在PHP中调试方法的介绍及应用

    使用Memcache在PHP中调试方法的介绍及应用 如果我们在网络开发中,特别是大访问量的web项目开发中,为了提高响应速度,减少数据查询运算,那么我们都会选用memcahce.首先我们必须要安装,接 ...

  9. InputStreamReader 和 OutputStreamWriter类使用方法简单介绍,及演示。

    InputStreamReader 和 OutputStreamWriter类使用方法简单介绍. 一.InputStreamReader类 InputStreamReader 将字节流转换为字符流.是 ...

随机推荐

  1. Stream的去重排序

    1.List<Integer>排序 List<Integer> list = new ArrayList<>();list.add(50);list.add(25) ...

  2. H5C3--边框阴影box-shadow

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 【笔记】LR11中关联设置

    LR中关联建议都手动进行,自动不好用,也容易出错. 在LR中我们什么要做关联:1.关联解决的是动态数据的参数化.2.关联的数据一定是服务器响应的数据.3.服务器响应过来的数据在后面的服务还要使用. 手 ...

  4. wpf样式与行为

  5. Chrome谷歌浏览器调试

    Chrome浏览器调试技巧  https://blog.csdn.net/u014727260/article/details/53231298

  6. list的基本使用

    转自:https://www.cnblogs.com/BeyondAnyTime/archive/2012/08/10/2631191.html vector :vector和built-in数组类似 ...

  7. C# 如何比较版本号大小

    最近遇到了一个数据迁移的问题,因为配置文件的结构发生变化,所以要把低版本的用户数据保存下来,存到最新版本中去. 这里就有一个比较版本号大小的问题了,网上乱七八糟算法一堆,大致意思就是用分割字符串的方法 ...

  8. ELK学习目录

    DAY1.elasticsearch和kibana环境搭建以及简单介绍 A:环境搭建配置 (1)java虚拟机安装:https://www.cnblogs.com/studybrother/p/108 ...

  9. awk notes

    字符串拼接 cat sql | awk '{print " ALTER TABLE  tableA   ALTER " $1 " TYPE " $2 " ...

  10. IDEA 创建文件夹总默认根节点问题解决

    上面是文件夹结构显示,如果勾掉,就是按层级显示,空目录不会自动折叠成一行 原文地址;https://blog.csdn.net/huangjunwei6/article/details/7150755 ...