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.,在开发中如果需要 ...
随机推荐
- [WPF]记一个Win8"缩放级别"设置导致的问题
这是我电脑的分辨率设置: 关键在于设置了缩放级别"较大",即150%的缩放. 接下来在WinForm中用各种方法取得的屏幕分辨率都是缩放之后的,但是这个时候的鼠标事件中鼠标位置也是 ...
- secureCRT的文件上传技巧
现在我们经常会习惯性的使用windows系统,但现在开发项目和维护中经常都在使用linux服务器,以为它的性能更强.更精简. 学习大数据的同志们和维护后端的同志们,推荐一下secureCRT软件,用起 ...
- winform:对dataGridView绑定的泛型List<T> 的简单CRUD
创建对象类,为所有成员封装字段,然后重载该类: 根据已有的对象类(类型参数)创建一个长度可以变化的List数组,并绑定数据源: 设置dataGridView的column属性:对应四个对象类创建相 ...
- day_02mysql表的约束设计
首先我们复习第一天的主要内容: sql分类 知识概述 1) DDL(Data Definition Language)数据定义语言 用来定义数据库对象:数据库,表,列等.关键字:create, dro ...
- Python爬虫初识
本文章是对网易云课堂中的Python网络爬虫实战课程进行总结.感兴趣的朋友可以观看视频课程.课程地址 爬虫简介 一段自动抓取互联网信息的程序 非结构化数据 没有固定的数据格式,如网页资料. 必须通过E ...
- csharp:获取 DNS、网关、子网掩码、IP
/// <summary> /// DNS.网关.子网掩码.IP /// 涂聚文 2015 /// </summary> public class IPAddressStrin ...
- jQuery源码学习笔记一
学习jQuery源码,我主要是通过妙味视频上学习的.这里将所有的源码分析,还有一些自己弄懂过程中的方法及示例整理出来,供大家参考. 我用的jquery v2.0.3版本. var rootjQuery ...
- css+div水平居中
实现div内容水平居中 实现方案一:margin:0 auto; div{ height:100px; width:100px; background:red; margin:0 auto; } &l ...
- Jquery插件网站持续添加。。。
Look Fro Less,Do More www.jq22.com
- css动画笔记
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...