Java 5 开始引入 Conccurent 软件包,提供完备的并发能力,对线程池有了更好的支持。其中,Executor 框架是最值得称道的。

Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,Executor.execute(Runnalbe) 。Executor在执行时使用内部的线程池完成操作。

一、创建线程池

Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

public static ExecutorService newFixedThreadPool(int nThreads)

创建固定数目线程的线程池。

public static ExecutorService newCachedThreadPool()

创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

public static ExecutorService newSingleThreadExecutor()

创建一个单线程化的Executor。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

 Executor executor = Executors.newFixedThreadPool(10);
Runnable task = new Runnable() { @Override public void run() { System.out.println("task over"); } }; executor.execute(task);

或者

 executor = Executors.newScheduledThreadPool(10);
ScheduledExecutorService scheduler = (ScheduledExecutorService) executor;
scheduler.scheduleAtFixedRate(task, 10, 10, TimeUnit.SECONDS);

二、ExecutorService与生命周期

ExecutorService扩展了Executor并添加了一些生命周期管理的方法。一个Executor的生命周期有三种状态,运行 ,关闭 ,终止 。Executor创建时处于运行状态。当调用ExecutorService.shutdown()后,处于关闭状态,isShutdown()方法返回true。这时,不应该再想Executor中添加任务,所有已添加的任务执行完毕后,Executor处于终止状态,isTerminated()返回true。

如果Executor处于关闭状态,往Executor提交任务会抛出unchecked exception RejectedExecutionException。

 ExecutorService executorService = (ExecutorService) executor;  

 while (!executorService.isShutdown()) {  

     try {  

         executorService.execute(task);  

     } catch (RejectedExecutionException ignored) {  

     }  

 }  

 executorService.shutdown();  

三、使用Callable,Future返回结果

Future代表一个异步执行的操作,通过get()方法可以获得操作的结果,如果异步操作还没有完成,则,get()会使当前线程阻塞。FutureTask实现了Future和Runable。Callable代表一个有返回值得操作。

 Callable func = new Callable(){  

     public Integer call() throws Exception {  

         System.out.println("inside callable");  

         Thread.sleep(1000);  

         return new Integer(8);  

     }         

 };        

 FutureTask futureTask  = new FutureTask(func);  

 Thread newThread = new Thread(futureTask);  

 newThread.start();  

 try {  

     System.out.println("blocking here");  

     Integer result = futureTask.get();  

     System.out.println(result);  

 } catch (InterruptedException ignored) {  

 } catch (ExecutionException ignored) {  

 }  

ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

例子:并行计算数组的和。

 package executorservice;  

 import java.util.ArrayList;  

 import java.util.List;  

 import java.util.concurrent.Callable;  

 import java.util.concurrent.ExecutionException;  

 import java.util.concurrent.ExecutorService;  

 import java.util.concurrent.Executors;  

 import java.util.concurrent.Future;  

 import java.util.concurrent.FutureTask;  

 public class ConcurrentCalculator {  

     private ExecutorService exec;  

     private int cpuCoreNumber;  

     private List> tasks = new ArrayList>();  

     // 内部类  

     class SumCalculator implements Callable {  

         private int[] numbers;  

         private int start;  

         private int end;  

         public SumCalculator(final int[] numbers, int start, int end) {  

             this.numbers = numbers;  

             this.start = start;  

             this.end = end;  

         }  

         public Long call() throws Exception {  

             Long sum = 0l;  

             for (int i = start; i < end; i++) {  

                 sum += numbers[i];  

             }  

             return sum;  

         }  

     }  

     public ConcurrentCalculator() {  

         cpuCoreNumber = Runtime.getRuntime().availableProcessors();  

         exec = Executors.newFixedThreadPool(cpuCoreNumber);  

     }  

     public Long sum(final int[] numbers) {  

         // 根据CPU核心个数拆分任务,创建FutureTask并提交到Executor  

         for (int i = 0; i < cpuCoreNumber; i++) {  

             int increment = numbers.length / cpuCoreNumber + 1;  

             int start = increment * i;  

             int end = increment * i + increment;  

             if (end > numbers.length)  

                 end = numbers.length;  

             SumCalculator subCalc = new SumCalculator(numbers, start, end);  

             FutureTask task = new FutureTask(subCalc);  

             tasks.add(task);  

             if (!exec.isShutdown()) {  

                 exec.submit(task);  

             }  

         }  

         return getResult();  

     }  

     public Long getResult() {  

         Long result = 0l;  

         for (Future task : tasks) {  

             try {  

                 // 如果计算未完成则阻塞  

                 Long subSum = task.get();  

                 result += subSum;  

             } catch (InterruptedException e) {  

                 e.printStackTrace();  

             } catch (ExecutionException e) {  

                 e.printStackTrace();  

             }  

         }  

         return result;  

     }  

     public void close() {  

         exec.shutdown();  

     }  

 }  

 Main

 int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11 };  

 ConcurrentCalculator calc = new ConcurrentCalculator();  

 Long sum = calc.sum(numbers);  

 System.out.println(sum);  

 calc.close();  
 四、CompletionService

在刚在的例子中,getResult()方法的实现过程中,迭代了FutureTask的数组,如果任务还没有完成则当前线程会阻塞,如果我们希望任意字任务完成后就把其结果加到result中,而不用依次等待每个任务完成,可以使CompletionService。生产者submit()执行的任务。使用者take()已完成的任务,并按照完成这些任务的顺序处理它们的结果 。也就是调用CompletionService的take方法是,会返回按完成顺序放回任务的结果,CompletionService内部维护了一个阻塞队列BlockingQueue,如果没有任务完成,take()方法也会阻塞。修改刚才的例子使用CompletionService:

 public class ConcurrentCalculator2 {  

     private ExecutorService exec;  

     private CompletionService completionService;  

     private int cpuCoreNumber;  

     // 内部类  

     class SumCalculator implements Callable {  

         ......  

     }  

     public ConcurrentCalculator2() {  

         cpuCoreNumber = Runtime.getRuntime().availableProcessors();  

         exec = Executors.newFixedThreadPool(cpuCoreNumber);  

         completionService = new ExecutorCompletionService(exec);  

     }  

     public Long sum(final int[] numbers) {  

         // 根据CPU核心个数拆分任务,创建FutureTask并提交到Executor  

         for (int i = 0; i < cpuCoreNumber; i++) {  

             int increment = numbers.length / cpuCoreNumber + 1;  

             int start = increment * i;  

             int end = increment * i + increment;  

             if (end > numbers.length)  

                 end = numbers.length;  

             SumCalculator subCalc = new SumCalculator(numbers, start, end);   

             if (!exec.isShutdown()) {  

                 completionService.submit(subCalc);  

             }  

         }  

         return getResult();  

     }  

     public Long getResult() {  

         Long result = 0l;  

         for (int i = 0; i < cpuCoreNumber; i++) {              

             try {  

                 Long subSum = completionService.take().get();  

                 result += subSum;             

             } catch (InterruptedException e) {  

                 e.printStackTrace();  

             } catch (ExecutionException e) {  

                 e.printStackTrace();  

             }  

         }  

         return result;  

     }  

     public void close() {  

         exec.shutdown();  

     }  

 }  

Java 并发编程中的 Executor 框架与线程池的更多相关文章

  1. Java并发编程(三)什么是线程池

    什么是线程池 学习编程的小伙伴们会经常听到“线程池”.“连接池”这类的词语,可是到底“池”是什么意思呢?我讲个故事大家就理解了:在很久很久以前有一家银行,一年之中只有一个客户来办理业务,随着时间的推移 ...

  2. Java并发编程(八):线程调度——线程池

    new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { / ...

  3. 【java并发编程实战】第八章:线程池的使用

    1.线程饥饿锁 定义:在线程池中,如果任务的执行依赖其他任务,那么可能会产生线程饥饿锁.尤其是单线程线程池. 示例: public class ThreadDeadStarveTest { publi ...

  4. Java 并发编程中的 CyclicBarrier 用于一组线程互相等待

    Java 5 引入的 Concurrent 并发库软件包中的 CyclicBarrier 是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point) ...

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

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

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

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

  7. Java并发编程中的设计模式解析(二)一个单例的七种写法

    Java单例模式是最常见的设计模式之一,广泛应用于各种框架.中间件和应用开发中.单例模式实现起来比较简单,基本是每个Java工程师都能信手拈来的,本文将结合多线程.类的加载等知识,系统地介绍一下单例模 ...

  8. Java并发编程中的若干核心技术,向高手进阶!

    来源:http://www.jianshu.com/p/5f499f8212e7 引言 本文试图从一个更高的视角来总结Java语言中的并发编程内容,希望阅读完本文之后,可以收获一些内容,至少应该知道在 ...

  9. Java并发编程中的相关注解

    引自:http://www.cnblogs.com/phoebus0501/archive/2011/02/21/1960077.html Java并发编程中,用到了一些专门为并发编程准备的 Anno ...

随机推荐

  1. lru缓存测试类

    package demo.mytest; import java.io.Serializable;import java.util.LinkedHashMap;import java.util.con ...

  2. ios retain copy 以及copy协议

    阅读本文之前首先了解Copy与Retain的区别: Copy是创建一个新对象,Retain是创建一个指针,引用对象计数加1. Copy属性表示两个对象内容相同,新的对象retain为1 ,与旧有对象的 ...

  3. classList属性和className的区别

    className的不方便之处: 在操作类名时,需要通过className属性添加,删除和替换类名.因为className中是一个字符串,所以即使只修改字符串一部分,也必须每次都设置整个字符串的值.( ...

  4. pycharm添加wordcloud模块时报错:error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools

    windows 7 32bit python3.6.3 32bit pycharm2018社区版 32bit 问题说明: 添加wordcloud模块时报错:error: Microsoft Visua ...

  5. (转)iOS 最佳实践

    本文转自http://www.jianshu.com/p/b0bf2368fb95 感谢作者和译者 iOS最佳实践 iOS最佳实践 译者注 本文翻译自 futurice 公司的 iOS Good Pr ...

  6. Experiments done

    喷重金属 换重金属溶液 荧光光合 备注 ASD 备注 高光谱 备注 泡EDTA 备注 电镜 备注 2018.12.19(day1) 2018.12.19(day1) 2018.12.18晚(day0) ...

  7. 【03】Chrome提示印象笔记剪藏插件"已停用不支持的扩展程序"怎么办?

    [03] Chrome提示印象笔记剪藏插件"已停用不支持的扩展程序"怎么办? 刚好也遇上了这个问题,百度了一下,以下是解决方法,亲测可行: 1.首先把需要安装的第三方插件,后缀.c ...

  8. Nmap手册

    转自:http://drops.xmd5.com/static/drops/tips-4333.html 0x00:说明 只是一个快速查询手册,理论的东西都没有补充,欢迎大家积极在评论区补充自己常用的 ...

  9. Linux基础命令详解-1

    本篇详解的命令有以下30个 1.cd 功能:切换工作目录 参数列表     2.ls 功能:查看目录里的内容 参数列表     3.mv 功能:  移动或重命名文件和目录 参数列表     4.pwd ...

  10. HDU 4280 Island Transport

    Island Transport Time Limit: 10000ms Memory Limit: 65536KB This problem will be judged on HDU. Origi ...