ExecutorService

Java.util.concurrent.ExecutorService接口代表一种异步执行机制,它能够在后台执行任务。因此ExecutorService与thread pool是非常相似的。事实上,在java.util.package包中ExecutorService的具体实现就是一个线程池的具体实现。

ExcutorService 例子

下面是一个简单的例子

ExecutorService executorService = Executors.newFixedThreadPool(10);

executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
}); executorService.shutdown();

首先,通过newFixedThreadPool()工厂方法创建一个ExecutorService的实例。这个方法创建了一个可以有10个线程执行任务的线程池。

第二,Runnable接口的匿名实现类作为参数被传递给execute()方法。Runable将会被ExecutorService中的一个线程来执行。

任务委托(Task Delegation)

下面的图片说明了一个线程委托一个任务给ExecutorService进行异步执行:



一旦,线程委托任务给ExecutorService,线程会独立任务的执行而继续自己之后的操作。

ExcutorService的使用说明

下面是委托任务给ExecutorService的一些不同的方式:

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny(…)
  • invokeAll(…)

下面来逐个看看这些方法。

  • execute(Runnable)

execute(Runnable) 方法接受一个java.lang.Runable对象的实例,并异步执行之。下面是一个使用ExecutorService执行Runnable的例子:

ExecutorService executorService = Executors.newSingleThreadExecutor();

executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
}); executorService.shutdown();

这种方式不能获得Runnable执行的结果,如果有这种需要,你将要使用Callable。

  • submit(Runnable)

submit(Runnable) 方法也接收一个Runnable接口的具体实现,并返回一个Future对象。Future对象可以用来检测Runable是否执行完成。

Future future = executorService.submit(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
}); future.get(); //returns null if the task has finished correctly.
  • submit(Callable)

submit(Callable)方法与submit(Runnable)方法相似,除了接收的参数有所不同。Callable实例非常类似于Runnable,不同的是call方法可以返回一个结果,Runnable.run()方法不能返回一个结果(因为是void类型),就算线程执行完了,成功了future.get()也只是得到null

可以通过submit(Callable)方法返回的Future对象获取Callable的结果。下面是一个使用Callable的例子:

Future future = executorService.submit(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
}); future.get(); //returns null if the task has finished correctly.

上面代码的输出结果是:

Asynchronous Callable
future.get() = Callable Result
  • invokeAny(…)

invokeAny()方法接收一个Callable对象或者Callable的子接口实例的集合作为参数,这个方法不会返回Future,但会返回集合中某一个Callable的结果。你不能确定你得到是哪个Callable的结果。只是已执行完成的Callable中的一个。

如果一个任务已经完成(或者抛出了异常),剩余的Callable任务将被取消。

下面是示例代码:

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set<Callable<String>> callables = new HashSet<Callable<String>>();

callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 3";
}
}); String result = executorService.invokeAny(callables); System.out.println("result = " + result); executorService.shutdown();
  • 示例代码将会打印给定的Callable集合中一个Callable任务返回的结果。我尝试执行了多次,结果是变化的。有时候是“Task1”,有时候是“Task 2”等。

  • invokeAll(…)

invokeAll()接收一个Callable对象的集合作为参数,该方法会调用你传给他的集合中的所有Callable对象。InvokeAll()会返回一个Future对象的列表,通过这个列表你可以获取每一个Callable执行的结果。

一个任务可能会因为一个异常而结束,因此这时任务并不是真正意义上执行成功了。这在Future上是没有办法来判断的。

invokeAll()处理一个任务的容器(collection),并返回一个Future的容器。两个容器具有相同的结构,这里提交的任务容器列表和返回的Future列表存在顺序对应的关系。

下面是示例代码:

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set<Callable<String>> callables = new HashSet<Callable<String>>();

callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 3";
}
}); List<Future<String>> futures = executorService.invokeAll(callables); for(Future<String> future : futures){
System.out.println("future.get = " + future.get());
} executorService.shutdown();

ExecutorService Shutdown

当你是使用完ExecutorService后,你应该关闭它,使得线程不能持续运行。例如,你的应用程序从main()方法开始并且你的主线程退出应用程序,这时如果存在激活状态的ExecutorService,你的应用程序将仍然会保持运行。ExecutorService中激活的线程会阻止JVM关闭。

为了终止ExecutorService中的线程,你需要调用shutdown()方法。ExecutorService不会立即关闭,但是它也不会接受新的任务,直到它里面的所有线程都执行完毕,ExecutorService才会关闭。所有提交到ExecutorService中的任务会在调用shutdown()方法之前被执行。

如果你想立即关闭ExecutorService,你可以调用shutdownNow()方法。这将会尝试立即停止所有正在执行的任务,并且忽略所有提交的但未被处理的任务。对于正在执行的任务是不能确定的,也许它们停止了,也行它们执行直到结束。

方法说明

1、shutdown():停止接收新任务,原来的任务继续执行

英文原意:关闭,倒闭;停工。 这里的意思是 关闭线程池与使用数据库连接池一样,每次使用完毕后,都要关闭线程池。

1、停止接收新的submit的任务;

2、已经提交的任务(包括正在跑的和队列中等待的),会继续执行完成;

3、等到第2步完成后,才真正停止;

2、shutdownNow():停止接收新任务,原来的任务停止执行

1、跟 shutdown() 一样,先停止接收新submit的任务;

2、忽略队列里等待的任务;

3、尝试将正在执行的任务interrupt中断;

4、返回未执行的任务列表;

说明:它试图终止线程的方法是通过调用 Thread.interrupt() 方法来实现的,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt() 方法是无法中断当前的线程的。所以,shutdownNow() 并不代表线程池就一定立即就能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。但是大多数时候是能立即退出的。

3、awaitTermination(long timeOut, TimeUnit unit):当前线程阻塞

timeout 和 TimeUnit 两个参数,用于设定超时的时间及单位

当前线程阻塞,直到:

  • 等所有已提交的任务(包括正在跑的和队列中等待的)执行完;
  • 或者 等超时时间到了(timeout 和 TimeUnit设定的时间);
  • 或者 线程被中断,抛出InterruptedException

然后会监测 ExecutorService 是否已经关闭,返回true(shutdown请求后所有任务执行完毕)或false(已超时)

区别

1、shutdown() 和 shutdownNow() 的区别

shutdown() 只是关闭了提交通道,用submit()是无效的;而内部该怎么跑还是怎么跑,跑完再停。

shutdownNow() 能立即停止线程池,正在跑的和正在等待的任务都停下了。

2、shutdown() 和 awaitTermination() 的区别

shutdown() 后,不能再提交新的任务进去;但是 awaitTermination() 后,可以继续提交。

awaitTermination()是阻塞的,返回结果是线程池是否已停止(true/false);shutdown() 不阻塞。

总结

1、优雅的关闭,用 shutdown()

2、想立马关闭,并得到未执行任务列表,用shutdownNow()

3、优雅的关闭,并允许关闭声明后新任务能提交,用 awaitTermination()

4、关闭功能 【从强到弱】 依次是:shuntdownNow() > shutdown() > awaitTermination()

ThreadPoolExecutor

Java.util.concurrent.ThreadPoolExecutor类是ExecutorSerivce接口的具体实现。ThreadPoolExecutor使用线程池中的一个线程来执行给定的任务(Runnable或者Runnable)。

ThreadPoolExecutor内部的线程池包含不定数量的线程。池中线程的数量由下面的这些变量决定:

  • corePoolSize
  • maximumPoolSize

当一个任务委托给线程池执行,此时如果池线程中线程数少于corePoolSize,即使池中有空闲的线程,线程池中也会创建一个新的线程。

如果任务队列是满的,corePoolSize个线程或者更多的且少于maximumPoolSize的线程正在运行,也会创建一个新的线程来执行任务。

下面图释ThreadPoolExecutor这种原理:

创建ThreadPoolExecutor

ThreadPoolExecutor有多种构造函数。例如:

int  corePoolSize  =    5;
int maxPoolSize = 10;
long keepAliveTime = 5000; ExecutorService threadPoolExecutor =
new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
);

除非你需要显示的给ThreadPoolExecutor指定这些参数,通常使用java.util.concurrent.Executor类中的工厂方法来创建实例。

【Java多线程】ExecutorService和ThreadPoolExecutor的更多相关文章

  1. java多线程系列:ThreadPoolExecutor源码分析

    前言 这篇主要讲述ThreadPoolExecutor的源码分析,贯穿类的创建.任务的添加到线程池的关闭整个流程,让你知其然所以然.希望你可以通过本篇博文知道ThreadPoolExecutor是怎么 ...

  2. Java多线程-线程池ThreadPoolExecutor构造方法和规则

    为什么用线程池 原文地址 http://blog.csdn.net/qq_25806863/article/details/71126867 有时候,系统需要处理非常多的执行时间很短的请求,如果每一个 ...

  3. java中 ExecutorService,Executor,ThreadPoolExecutor的用法

    package com; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import ...

  4. java多线程系列:ThreadPoolExecutor

    ThreadPoolExecutor自定义线程池 开篇一张图(图片来自阿里巴巴Java开发手册(详尽版)),后面全靠编 好了要开始编了,从图片中就可以看到这篇博文的主题了,ThreadPoolExec ...

  5. java多线程ExecutorService

    1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { ...

  6. java 多线程 一个博客

    http://blog.csdn.net/a352193394/article/category/2563875 Java多线程之~~~线程安全容器的非阻塞容器 在并发编程中,会经常遇到使用容器.但是 ...

  7. 转:java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例

    java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例 1.CountDownLatch:一个同步工具类,它允许一个或多个线程一 ...

  8. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

  9. java多线程系类:JUC线程池:01之线程池架构

    概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构 ...

  10. Java多线程开发技巧

    很多开发者谈到Java多线程开发,仅仅停留在new Thread(...).start()或直接使用Executor框架这个层面,对于线程的管理和控制却不够深入,通过读<Java并发编程实践&g ...

随机推荐

  1. 学习JS的第二天

    一.数据类型间的转换 主要:数字与字符串之间的转换 1.隐式转换 // console.log(1==true);[] 字符串与数字相加,其结果就是字符串  类似于字符串拼接 concole.log( ...

  2. java开发环境搭建,配置

    java开发环境搭建 下载jdk8的地址 是oracle的 安装JDK 下载电脑对应的版本 双击安装JDK 记住安装路径 配置环境变量 我的电脑/此电脑 右键属性 高级设置 环境变量 点击新建 变量名 ...

  3. 96-00年CPU功耗感知调度研究

    最近读了一些1996-2000年的通过调度来降低cpu能耗的文章,主要文章有[1] [2] [3] [4] [5], 简单总结一些该时期单核CPU功耗感知的调度策略. 该时期还出现了很多关于低功耗电路 ...

  4. [hdu6761]Minimun Index

    $lyndon\ word$(以下简写为Lw):对于一个字符串s,其为Lw当且仅当其的最小后缀为自身 性质:若$u<v$为LW,那么$uv$也为Lw(反证法即可证) $lyndon$分解:将一个 ...

  5. github文件下载加速器

    https://d.serctl.com/?dl_start

  6. idea创建 springboot工程(支持jsp)

    以前学springboot以前想搭建一个支持jsp的项目一直弄不上,现在发现用maven创建一个项目然后改成springboot效果一样的 https://blog.csdn.net/gisboygo ...

  7. 微信小程序中途加入云开发之坑

    一开始未使用云开发的小程序项目,之后想使用云开发能力时,要先删除对应在开发者工具中的项目(先压缩备份源码!),再用开发者工具重新创建,很多时候都需要用这种方式进行处理

  8. Docker之容器化学习之路v20.10.3

    Docker概述 **本人博客网站 **IT小神 www.itxiaoshen.com Docker文档官网 Docker是一个用于开发.发布和运行应用程序的开放平台.Docker使您能够将应用程序与 ...

  9. 记一次 android 线上 oom 问题

    背景 公司的主打产品是一款跨平台的 App,我的部门负责为它提供底层的 sdk 用于数据传输,我负责的是 Adnroid 端的 sdk 开发. sdk 并不直接加载在 App 主进程,而是隔离在一个单 ...

  10. 面试官问我HTTP,我真的是

    面试官:今天要不来聊聊HTTP吧? 候选者:嗯,HTTP「协议」是客户端和服务器「交互」的一种通迅的格式 候选者:所谓的「协议」实际上就是双方约定好的「格式」,让双方都能看得懂的东西而已 候选者:所谓 ...