(转)java并发之Executor
场景: 线程池在面试时候经常会碰到,在工作中用的场景更多,所以很有必要弄清楚。
1 简介
Java自1.5以来加入了处理一批线程的方法,也就是java并发包里的Executor。本文主要介绍ExecutorService的用法,Runable和Callable的用法以及ExecutorCompletionService的用法。
使用Executor来执行多个线程的好处是用来避免线程的创建和销毁的开销,以提升效率。
因此如果某些场景需要反复创建线程去处理同类事务的话,可以考虑使用线程池来处理。
其实Executor本身并不实现纯种池的功能,只是提供了获取ExecutorService的方法,而ExecutorService才是真正处理线程池相关逻辑的类。
Executor下获取ExecutorService 的方法有很多,用于获取各种不同的纯种池,如单线程线程池、固定线程数的线程池等,不过最终还是调用ExecutorService的构造函数来创建,如下:
public ThreadPoolExecutor(int corePoolSize,//最少线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//线程池满后,后续线程的等待时间
TimeUnit unit,//等待时间的单位
BlockingQueue<Runnable> workQueue,//等待线程队列
ThreadFactory threadFactory)//线程生产工厂
1.1 Runable
通过以上方法就可以创建一个线程池方法,可以限制线程的数量和等待队列中线程的等待时间等。然后如果要通过这个线程池来执行线程:
executorService.execute(new Runnable() { @Override
public void run() {
System.out.println("Execute in pool:" + Thread.currentThread().getId());
}
});
通过execute()方法的执行是异步的,无法知道线程什么时候执行完毕。
如果要想知道线程是否执行完毕,可以通过另外一个方法submit()来执行,然后获取到一个future对象, 然后通过get()方法来判断是否执行完毕:
Future<?> future = executorService.submit(new Runnable() { @Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Execute in pool:" + Thread.currentThread().getId());
}
});
try {
if(future.get()==null){
System.out.println("finish!!!");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
但是通过这种方式只能知道线程是否执行完毕,却做不到将各线程的处理结果返回做归并处理。
要实现这个目的可以使用Callable接口来封装任务逻辑,Callable和Runable的 唯一区别就是它支持返回处理结果:
1.2 Callable
可以处理线程执行完之后的返回结果。
Future<?> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "hello callable!";
} });
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
其中call()方法中返回的值,就是Future对象get()到的值。但是如果有多个线程在处理,然后要将这些线程的处理结果归并怎么做呢?
当然可以使用ExecutorService来获取每个放到线程池的线程的Future对象,然后遍历的去get()然后去做归并处理。但是显然这种方法并不能做到先完成的就被先归并,而是取决于遍历到的时间,这显然降低了处理效率。
要处理这种场景,可以使用另外一个Service–ExecutorCompletionService:
1.3 ExecutorCompletionService
package test03; import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import org.junit.Test; public class ExecutorCompletionServiceTest { @Test
public void test() {
ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletionService<Long> completionService = new ExecutorCompletionService<Long>(executorService);
for (int i = 0; i < 4; i++) {
long sleep = (5 - i) * 1000;
completionService.submit(new ExeWorker(sleep)); //使用CompletionService来调用线程,可以做到先完成先处理
}
for(int i=0;i<4;i++){
try {
System.out.println(completionService.take().get()+" Get!"); //获取线程处理后的返回结果
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
} } } class ExeWorker implements Callable<Long> {
private long sleep; public ExeWorker(long sleep) {
this.sleep = sleep;
} @Override
public Long call() throws Exception {
System.out.println(sleep + " Executing!");
Thread.sleep(sleep);
System.out.println(sleep + " Done!");
return sleep;
} }
以线程的sleep时间为线程名称,然后输出结果为:
可以看出后面那个循环获取处理结果的地方的确是按先完成先返回的方式来实现。这种方法的一个约束就是需要知道有多少个线程在处理。(不是很理解)
其实CompletionService底层是通过一个BlockingQueue来存放处理结果,你也可以使用它自身封装好的带超时的poll方法来获取返回结果。
(转)java并发之Executor的更多相关文章
- java并发之Future与Callable使用
java并发之Future与Callable使用 这篇文章需要大家知道线程.线程池的知识,尤其是线程池. 有的时候我们要获取线程的执行结果,这个时候就需要用到Callable.Future.Futur ...
- (Java 多线程系列)Java 线程池(Executor)
线程池简介 线程池是指管理同一组同构工作线程的资源池,线程池是与工作队列(Work Queue)密切相关的,其中在工作队列中保存了所有等待执行的任务.工作线程(Worker Thread)的任务很简单 ...
- java并发之固定对象与实例
java并发之固定对象与实例 Immutable Objects An object is considered immutable if its state cannot change after ...
- Java并发之BlockingQueue的使用
Java并发之BlockingQueue的使用 一.简介 前段时间看到有些朋友在网上发了一道面试题,题目的大意就是:有两个线程A,B, A线程每200ms就生成一个[0,100]之间的随机数, B线 ...
- Java 并发编程——Executor框架和线程池原理
Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...
- (转)java并发编程--Executor框架
本文转自https://www.cnblogs.com/MOBIN/p/5436482.html java并发编程--Executor框架 只要用到线程,就可以使用executor.,在开发中如果需要 ...
- 深入理解Java并发之synchronized实现原理
深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...
- Java 并发编程——Executor框架和线程池原理
Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...
- Java并发之Semaphore的使用
Java并发之Semaphore的使用 一.简介 今天突然发现,看着自己喜欢的球队发挥如此的棒,然后写着博客,这种感觉很爽.现在是半场时间,就趁着这个时间的空隙,说说Java并发包中另外一个重量级的类 ...
随机推荐
- 浅析DES与AES、RSA三种典型加密算法的比较
DES与AES的比较 自DES 算法公诸于世以来,学术界围绕它的安全性等方面进行了研究并展开了激烈的争论.在技术上,对DES的批评主要集中在以下几个方面: 1.作为分组密码,DES 的加密单位仅有64 ...
- vscode中使用markdown
vscode中使用markdown vscode 是微软推出一款轻量级的文本编辑工具,类似于sublime,由于其拥有丰富的插件,安装使用也非常简单,所以深受广大程序员的喜爱. markdown 是一 ...
- 深入探索C++对象模型(六)
执行期语意学(Runtime Semantics) 对象的构造和析构(Object Constructor and Destructor) 一般而言,constructor和destructor的安插 ...
- Mybatis学习(二) - CRUD操作(增删改查操作)
直接上例子: 1.项目结构: 2.具体代码及配置 User.java package com.mybatis.bean; public class User { private int id; pri ...
- 在windows下使用Qt5开发GTK3图形界面应用程序
首先,去MSYS2官网下载MSYS2环境并安装在C:/mysys64下,我安装的是64位的. 进入MSYS命令行执行: pacman -S mingw-w64-x86_64-gtk3 pacman - ...
- JS学习笔记——数组去重
<script type="text/javascript"> //indexOf"是ECMAScript5方法,IE8以下不支持,需多写兼容低版本浏览器代码 ...
- Step by Step 用Azure Automation 来开虚机(ARM)
使用Azure Automation来自动化处理各种重复的耗时的云管理任务从而帮助云运维人员提升效率,帮助降低运营成本. 具体相关的介绍以及怎样利用Azure Automation来完成定期开关虚拟机 ...
- ffmpeg最全的命令参数
Hyper fast Audio and Video encoderusage: ffmpeg [options] [[infile options] -i infile]... {[outfile ...
- 搭建开源java博客并通过域名访问
这个博客系统是王爵在GitHub上开源的,通过简单几步就可以部署成功. 前面几步可以参照如下几个链接: 1.https://www.qcloud.com/community/article/29008 ...
- MySQL如何有效的存储IP地址及字符串IP和数值之间如何转换
mysql> select inet_aton('192.168.0.1'); +--------------------------+ | inet_aton('192.168.0.1') | ...