场景: 线程池在面试时候经常会碰到,在工作中用的场景更多,所以很有必要弄清楚。

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的更多相关文章

  1. java并发之Future与Callable使用

    java并发之Future与Callable使用 这篇文章需要大家知道线程.线程池的知识,尤其是线程池. 有的时候我们要获取线程的执行结果,这个时候就需要用到Callable.Future.Futur ...

  2. (Java 多线程系列)Java 线程池(Executor)

    线程池简介 线程池是指管理同一组同构工作线程的资源池,线程池是与工作队列(Work Queue)密切相关的,其中在工作队列中保存了所有等待执行的任务.工作线程(Worker Thread)的任务很简单 ...

  3. java并发之固定对象与实例

    java并发之固定对象与实例 Immutable Objects An object is considered immutable if its state cannot change after ...

  4. Java并发之BlockingQueue的使用

    Java并发之BlockingQueue的使用 一.简介 前段时间看到有些朋友在网上发了一道面试题,题目的大意就是:有两个线程A,B,  A线程每200ms就生成一个[0,100]之间的随机数, B线 ...

  5. Java 并发编程——Executor框架和线程池原理

    Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...

  6. (转)java并发编程--Executor框架

    本文转自https://www.cnblogs.com/MOBIN/p/5436482.html java并发编程--Executor框架 只要用到线程,就可以使用executor.,在开发中如果需要 ...

  7. 深入理解Java并发之synchronized实现原理

    深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...

  8. Java 并发编程——Executor框架和线程池原理

    Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...

  9. Java并发之Semaphore的使用

    Java并发之Semaphore的使用 一.简介 今天突然发现,看着自己喜欢的球队发挥如此的棒,然后写着博客,这种感觉很爽.现在是半场时间,就趁着这个时间的空隙,说说Java并发包中另外一个重量级的类 ...

随机推荐

  1. ubuntu下配置Apache+mod_wsgi+Django项目(个人测试)

    经过了一个星期的摸索,查找资料以及实验,我搭建的环境基本能用(还有就是Django后台的静态文件加载的问题) 这里面只是介绍一下我的过程,因为对应Apache还不是很熟练,特别是配置文件.只能供大家参 ...

  2. 基于C#的接口自动化测试(一)

    其实就是找个地方然后给关键的代码做个笔记什么的-- 字符串访问API接口,访问方法为POST: string url = URL; string RequestParam = Param; strin ...

  3. 基于angularJs的单页面应用seo优化及可抓取方案原理分析

    公司使用angularJs(以下都是指ng1)框架做了互联网应用,之前没接触过seo,突然一天运营那边传来任务:要给网站做搜索引擎优化,需要研发支持.搜了下发现单页面应用做seo比较费劲,国内相关实践 ...

  4. 原生js在绑定事件时不用获取id名可直接用id名绑定???

    <div id="btn"> 点击 </div> <script> btn.onclick=function(){ console.log(bt ...

  5. PHP设计模式:抽象工厂

    示例代码详见https://github.com/52fhy/design_patterns 抽象工厂 抽象工厂(Abstract Factory)是应对产品族概念的.比如说,每个汽车公司可能要同时生 ...

  6. web移动端布局方式整理

    写H5页面一直写的有点随意,只是保证了页面在各个屏幕下显示良好,却没有保证到在各个屏幕下是等比例放大或者缩小.这些天在写一些页面,试着看看能不能写出等比例放大缩小的页面,发现不容易啊,在网上找了一些文 ...

  7. nginx常用配置系列-HTTPS配置

    接上篇,nginx配置系列 HTTPS现在已经很流行,特别是AppStore上线的应用要求使用HTTPS进行通信,出于安全考虑也应该使用HTTPS,HTTPS配置需要准备证书文件,现在也有很多免费证书 ...

  8. CSS学习笔记10 相对定位,绝对定位与固定定位

    文档流中的元素的位置由元素在 (X)HTML 中的位置决定,这就是最原始的普通流,前面讲到的浮动CSS学习笔记08 浮动可以改变元素在文档流中的位置,除了这个我们还可以通过使用CSS的position ...

  9. 【Android Developers Training】 63. 定义形状

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  10. js字符串转换为数字 总结

    a. 将一个字符串转换为数字的一种缺少些技巧但是很清楚明白的方法就是:把Number()构造函数作为一个函数来调用:  var number = Number(string_value); b. pa ...