Java并发编程--4.Executor框架
简介
Executor框架是启动,管理线程的API, 它的内部实现是线程池机制,它有很多好处,比如使任务提交和任务执行解耦合,防止this逃逸;
它的主要API包括: Executor, Executors, ExecutorService , Callable, Future, CompletionService, ThreadPoolExecutor
ExecutorService 生命周期
一个Executor的生命周期有三种状态: 运行状态,关闭状态,终止状态; ExecutorService中添加了生命周期管理的方法 Executor创建时, 处于运行状态; 当调用shutdown()后,处于关闭状态,停止接受新的线程,并执行已接受的线程任务; 所有任务执行完成后,处于终止状态
Executors 创建线程池
newFixedThreadPool : 创建定长的线程池, 最多有固定数量的线程, 如果还有创建新的线程,需要放到队列中等待, 直到有线程从池中移出
newCachedThreadPool :可缓存的线程池, 如果现有线程没有可用,则创建一个新线程并添加到池中。60 秒钟未被使用的线程会被移除
newSingleThreadExecutor : 只创建一个工作线程, 当工作线程异常结束, 会重新创建一个线程
newScheduledThreadPool: 创建定长的线程池, 支持定时的任务执行
线程池执行Runnable的例子
public class MyExecutors {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++){
executorService.execute(new MyRunnable());
} executorService.shutdown();
} } class MyRunnable implements Runnable { @Override
public void run() {
System.out.println(Thread.currentThread().getName() + "被调用");
}
}
Callable和Future 携带结果的任务
Callable: 带有返回值的任务
Future: 保存一个任务执行后的结果
下面给出一个例子:
public class MyCallable {
public static void main(String[] args){
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> resultList = new ArrayList<Future<String>>(); //创建10个任务并执行
for (int i = 0; i < 10; i++){
Future<String> future = executorService.submit(new TaskWithResult(i)); //将任务执行结果存储到List中
resultList.add(future);
} //遍历任务的结果
for (Future<String> fs : resultList){
try{
while(!fs.isDone());//Future返回如果没有完成,则一直循环等待,直到Future返回完成
System.out.println(fs.get()); //打印各个线程(任务)执行的结果 }catch(Exception e){
e.printStackTrace();
}finally{
//启动一次顺序关闭,执行以前提交的任务,但不接受新任务
executorService.shutdown();
}
}
}
} class TaskWithResult implements Callable<String>{
private int id; public TaskWithResult(int id){
this.id = id;
} /**
* 任务的具体过程,一旦任务传给ExecutorService的submit方法,
* 则该方法自动在一个线程上执行
*/
public String call() throws Exception {
System.out.println("call()方法被自动调用!!! " + Thread.currentThread().getName());
//该返回结果将被Future的get方法得到
return "call()方法被自动调用,任务返回的结果是:" + id + " " + Thread.currentThread().getName();
}
}
CompletionService完成服务,打包结果
任意任务完成后就把其加到结果中, 调用CompletionService的take()方法,返回 按任务的完成顺序 封装的结果, 像是一个打包的Future
下面给出一个例子
public class MyCompletionService {
public static void main(String[] args){
ExecutorService executorService = Executors.newCachedThreadPool(); //构造函数传入一个Executor
CompletionService<String> completionService = new ExecutorCompletionService<String>(executorService); //创建10个任务并执行
for (int i = 0; i < 10; i++){
if(!executorService.isShutdown()) {
//由CompletionService执行任务
completionService.submit(new Result0());
} } //把多个任务的结果加起来
String result = "2_";
try {
for (int i = 0; i < 10; i++) {
// 如果任务未完成,则该任务的take()会阻塞
String s = completionService.take().get();
result += s;
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} //输出最终的计算结果
System.out.println(result); try{ }catch(Exception e){
e.printStackTrace();
}finally{
//启动一次顺序关闭,执行以前提交的任务,但不接受新任务
executorService.shutdown();
}
}
} class Result0 implements Callable<String>{ /**
* 任务的具体过程,一旦任务传给ExecutorService的submit方法,
* 则该方法自动在一个线程上执行
*/
public String call() throws Exception {
//该返回结果将被Future的get方法得到
return "1";
}
}
ThreadPoolExecutor自定义线程池
ThreadPoolExecutor 有多个构造方法创建线程池,下面是一个构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
参数说明:
corePoolSize:线程池中所保存的核心线程数,包括空闲线程。
maximumPoolSize:池中允许的最大线程数。
keepAliveTime:线程池中的空闲线程所能持续的最长时间。
unit:持续时间的单位。
workQueue:任务执行前保存任务的队列,仅保存由execute方法提交的Runnable任务。
添加任务的过程
1、池中的线程数量少于corePoolSize,即使线程池中有空闲线程,也会创建一个新的线程来执行新添加的任务; 2、池中的线程数量大于等于corePoolSize,但缓冲队列未满,则将新添加的任务放到workQueue中,
线程池中有线程空闲出来后依次将缓冲队列中的任务交付给空闲的线程执行;
如果里面有线程的空闲时间超过了keepAliveTime,就将其移除线程池 3、池中的线程数量大于等于corePoolSize,且缓冲队列workQueue已满,但线程数量小于maximumPoolSize,则会创建新的线程来处理被添加的任务; 4、如果线程池中的线程数量等于了maximumPoolSize,通过设置饱和策略处理,也即是设置6个参数构造函数的第6个参数RejectedExecutionHandler
总体流程就是: 先看线程池中的线程数量是否大于corePoolSize,再看缓冲队列workQueue是否满,最后看线程池中的线程数量是否大于maximumPoolSize
排队策略
无界队列: 采用预定义容量的LinkedBlockingQueue,理论上是该缓冲队列可以对无限多的任务排队,newFixedThreadPool采用的便是这种策略
有界队列: 一般使用ArrayBlockingQueue制定队列的长度
同步移交 :SynchronousQueue 跳过队列,将任务从生产者直接交给消费者,如果不存在可用于立即运行任务的线程,会构造一个新的线程来处理新添加的任务,通常是无界的,newCa chedThreadPool采用的便是这种策略。
使用案例
public class MyThreadPoolExecutor {
public static void main(String[] args){
//创建等待队列
BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(20); //创建线程池,池中保存的线程数为3,允许的最大线程数为5
ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,50,TimeUnit.MILLISECONDS,bqueue); //创建七个任务
Runnable t1 = new MyThread();
Runnable t2 = new MyThread();
Runnable t3 = new MyThread();
Runnable t4 = new MyThread();
Runnable t5 = new MyThread();
Runnable t6 = new MyThread();
Runnable t7 = new MyThread(); //每个任务会在一个线程上执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
pool.execute(t7); //关闭线程池
pool.shutdown();
}
} class MyThread implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName() + "正在执行。。。");
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
控制台输出:
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-2正在执行。。。
可以看出7个任务在3个线程上执行
Java并发编程--4.Executor框架的更多相关文章
- Java 并发编程 -- Fork/Join 框架
概述 Fork/Join 框架是 Java7 提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架.下图是网上流传的 Fork Join 的 ...
- Java并发——线程池Executor框架
线程池 无限制的创建线程 若采用"为每个任务分配一个线程"的方式会存在一些缺陷,尤其是当需要创建大量线程时: 线程生命周期的开销非常高 资源消耗 稳定性 引入线程池 任务是一组逻辑 ...
- java并发编程:Executor、Executors、ExecutorService
1.Executor和ExecutorService Executor:一个接口,其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable comma ...
- Java并发编程之——Amino框架
Amino框架是一个采用无锁方式实现并行计算的框架,可惜的是,网上关于Amino框架的介绍甚少.根据所掌握的资料,稍微总结一下: 1. 锁机制到无锁机制 锁机制可以确保程序和数据的线程安全,但是锁是一 ...
- Java并发编程--Fork/Join框架使用
上篇博客我们介绍了通过CyclicBarrier使线程同步,可是上述方法存在一个问题,那就是假设一个大任务跑了2个线程去完毕.假设线程2耗时比线程1多2倍.线程1完毕后必须等待线程2完毕.等待的过程线 ...
- java并发编程-Executor框架
Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,Completion ...
- Java 并发编程——Executor框架和线程池原理
Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...
- 那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结
<Java并发编程实战>和<Java并发编程的艺术> Executor框架小结 1.在线程中如何执行任务 (1)任务执行目标: 在正常负载情况下,服务器应用 ...
- (转)java并发编程--Executor框架
本文转自https://www.cnblogs.com/MOBIN/p/5436482.html java并发编程--Executor框架 只要用到线程,就可以使用executor.,在开发中如果需要 ...
随机推荐
- 我用ASP.NET缓存之OutputCache
[我的理解]页面缓存常用在网站上.Web应用系统上也用,但由于Web系统常与数据库打交道.时效性要求蛮强的,所以是否能用缓存得具体情况具体分析(很喜欢这句话“具体情况具体分析”,很符合国人的中庸之道) ...
- 使用springcloud gateway搭建网关(分流,限流,熔断)
Spring Cloud Gateway Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 ...
- Maven配置私服仓库
首先就是,最基本的打开maven的配置文件,上面是我自己的习惯,多留一个以备不坏 打开setting配置文件,来修改路径(本人不习惯将所有软件放在C盘,一般都是单独存放盘) 接下来就是公司给你的账户和 ...
- 算法 - 排序数组中与x最近一点
条件: a[j] + a[j+1] < x*2 int findClosestPoint(int x,int a []) { int res = 0; int j = 0; while(j< ...
- swiper h5学习
http://www.swiper.com.cn/ 较多用于移动端
- C# Visual 快捷键
编辑系 代码提示 Ctrl + J激活VS默认代码提示功能.ReSharper等插件会建议你修改快捷键. 导入命名空间 个人认为Ctrl + .是最方便的,复杂点的是Alt + Shift + F ...
- Android实战——GreenDao3.2的使用,爱不释手
1前言 GreenDao是一款操作数据库的神器,经过了2.0版本的升级后,已经被广泛的开发者使用.确实是很好用,入门简单,可以剩去了数据库的建表操作和数据库SQL的编写,博主用了一次之后爱不释手,和以 ...
- 解决提交项目到GitHub,报错Please tell me who you are的问题
描述: 在第一次提交项目到GitHub时,出现了以下问题 看了一下错误原因:Run git config --global user.email "you@example.com" ...
- LGWR和DBWn的触发条件
Rolling Forward(前滚) Oracle启动实例并加载数据库,然后通过Online Redologs中的重做日志,重现实例崩溃前对数据库的修改操作.在恢复过程中对于已经提交的事务,但尚未写 ...
- Oracle EBS PO退货失败
无法读取例程 &ROUTINE 中配置文件选项 INV_DEBUG_TRACE 的值. 系统-配置文件-地点层 INC%调试%踪 是 select * from po_interface_e ...