(转)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并发包中另外一个重量级的类 ...
随机推荐
- javascript中event.clientX和event.clientY用法的注意事项
今天做项目用到了event.clientX和event.clientY,给元素定位,用定位的时候,让top和left等于事件元素的的坐标 <!DOCTYPE html> <html& ...
- 从《海贼王》的视角走进BAT的世界(百度/阿里/腾讯)
在伟大航路后半段--新世界,有三位君临天下的海上枭雄,就犹如海上皇帝一般,被人们称为"三皇",他们是--洛宾·里(Robin·Li),杰克·马(Jack·Ma)和波尼(pony)! ...
- 主要排序算法的Java实现
最近温习算法,花点时间写了点代码验证下,在这里做个存档. package com.chrischeng.hacker; import java.util.*; /** * Created by Yex ...
- [css 实践篇] CSS box-orient
定义和用法 box-orient 属性规定框的子元素应该被水平或垂直排列. 提示:水平框中的子元素从左向右进行显示,而垂直框的子元素从上向下进行显示.不过,box-direction 和 box-or ...
- loadrunner 手工参数拼接与l oadrunner的url编码
Acction() { //演示需要的一些变量,提前声明 char *name = "yezi_zh"; "; char *work = "engin" ...
- 两种代理模式(JDK和Cglib)实例
CGlib代理模式: package CGLIB; import java.lang.reflect.Method; import JDK.Test; import net.sf.cglib.prox ...
- 框架基础:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码
距离上一篇博客书写,又过去了大概几个月了,这段时间暂时离开了这个行业,让大脑休息一下.一个人旅行,一个人休息,正好也去完成一个目标 --- 拥有自己的驾照.当然,也把自己晒的黑漆马虎的.不过这一段时间 ...
- 【Android Developers Training】 63. 定义形状
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- jQery的链式操作和商城简易导航栏
今天要记录的是jq的一些简单操作.项目的需求是做一个导航栏,单机不同的商品名称链接,显示相应的内容.用js来写的话相对代码量要多一些,选择jqrey一行则可以搞定,下面呢是我的代码和效果图 这个是初始 ...
- 一篇文章学懂Shell脚本
Shell脚本,就是利用Shell的命令解释的功能,对一个纯文本的文件进行解析,然后执行这些功能,也可以说Shell脚本就是一系列命令的集合.Shell可以直接使用在win/Unix/Linux上面, ...