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.,在开发中如果需要 ...
随机推荐
- 算法:QQ等级换算成皇冠太阳星星月亮
/// <summary> /// 等级换算成图标分布 /// 以QQ的形式计算 /// 2^(2*0) /1 /// 2^(2*1) /4 /// 2^(2*2) /16 ...
- 采用DTO和DAO对JDBC程序进行进一步优化
采用DTO和DAO对JDBC程序进行进一步优化 DTO:数据传输对象,主要用于远程调用等需要远程调用对象的地方DAO:数据访问对象,主要实现封装数据库的访问,通过它可以把数据库中的表转换成DTO类 引 ...
- Django基础八之cookie和session
一 会话跟踪 我们需要先了解一下什么是会话!可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应.例如你给10086打个电话,你就是客户端,而10086服务人员就是服务器 ...
- PHP CURL库学习
基本请求步骤 : // . 初始化 $ch = curl_init(); // . 设置选项,包括URL curl_setopt($ch, CURLOPT_URL, "http://www. ...
- github上的项目发布成静态网页
代码上传成功之后就可以发布静态网页了,细心的童鞋应该已经看到我上传的代码在根目录就有一个html文件(发布其他情况没试过,感兴趣自己去尝试),发布的时候选择仓库即自动识别了. 第一步:点击settin ...
- 在JS中如何把毫秒转换成规定的日期时间格式
最近做的一个项目,从后台查询到日期数据传到前台JS中遍历之后显示的是以"14"开头的毫秒数,刚开始想用"simpleDateFormat"类处理,结果显示都不显 ...
- opencv图像处理时使用文件输入流ifstream批量读取图片
简介: 在利用opencv进行图像处理时,通常需要批量读取图片,然后做相应的处理,我们可以用C++文件的输入流来进行图片的读取,这要求我们应该事先,将图片图片名生成txt文件,具体请参见之前的博文[u ...
- leveldb源码分析--WriteBatch
从[leveldb源码分析--插入删除流程]和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批 ...
- Oracle EBS 复制用户职责
DECLARE l_new_user_name ) := 'HAND_HYL'; l_new_password ) :'; l_new_user_id NUMBER; l_user_name_copy ...
- python处理excel(一):读
功能:读取一个excel里的第2个sheet,将该sheet的内容全部输出. #coding=utf8 import xlrd def read_excel(): workbook = xlrd.op ...