public interface CompletionService<V> {  

    Future<V> submit(Callable<V> task);  

    Future<V> submit(Runnable task, V result);  

    /**  

     * Retrieves and removes the Future representing the next  

     * completed task, waiting if none are yet present.  

     *  

     * @return the Future representing the next completed task  

     * @throws InterruptedException if interrupted while waiting  

     * 阻塞/  

    Future<V> take() throws InterruptedException;  

      /**  

     * Retrieves and removes the Future representing the next  

     * completed task or <tt>null</tt> if none are present.  

     *  

     * @return the Future representing the next completed task, or  

     *         <tt>null</tt> if none are present  

     * 非阻塞/  

    Future<V> poll();  

}  

CompletionService 也不是到处都能用,它不适合处理任务数量有限但个数不可知的场景。例如,要统计某个文件夹中的文件个数,在遍历子文件夹的时候也会“递归地”提交新的任务,但最后到底提交了多少,以及在什么时候提交完了所有任务,都是未知数,无论 CompletionService 还是线程池都无法进行判断。这种情况只能直接用线程池来处理。

CompletionService 接口的实例可以充当生产者和消费者的中间处理引擎,从而达到将提交任务和处理结果的代码进行解耦的目的。生产者调用submit 方法提交任务,而消费者调用 poll(非阻塞)或 take(阻塞)方法获取下一个结果:这一特征看起来和阻塞队列(BlockingQueue)类似,两者的区别在于 CompletionService 要负责任务的处理,而阻塞队列则不会。

在 JDK 中,该接口只有一个实现类 ExecutorCompletionService,该类使用创建时提供的 Executor 对象(通常是线程池)来执行任务,然后将结果放入一个阻塞队列中。

1. 背景

在Java5的多线程中,可以使用Callable接口来实现具有返回值的线程。使用线程池的submit方法提交Callable任务,利用submit方法返回的Future存根,调用此存根的get方法来获取整个线程池中所有任务的运行结果。

方法一:如果是自己写代码,应该是自己维护一个Collection保存submit方法返回的Future存根,然后在主线程中遍历这个Collection并调用Future存根的get()方法取到线程的返回值。

方法二:使用CompletionService类,它整合了Executor和BlockingQueue的功能。你可以将Callable任务提交给它去执行,然后使用类似于队列中的take方法获取线程的返回值。

2. 实现代码

package com.clzhang.sample.thread;  

import java.util.*;  

import java.util.concurrent.BlockingQueue;  

import java.util.concurrent.Callable;  

import java.util.concurrent.CompletionService;  

import java.util.concurrent.ExecutorCompletionService;  

import java.util.concurrent.ExecutorService;  

import java.util.concurrent.Executors;  

import java.util.concurrent.Future;  

import java.util.concurrent.LinkedBlockingQueue;  

public class ThreadPoolTest4 {  

    // 具有返回值的测试线程  

    class MyThread implements Callable<String> {  

        private String name;  

        public MyThread(String name) {  

            this.name = name;  

        }  

        @Override  

        public String call() {  

            int sleepTime = new Random().nextInt(1000);  

            try {  

                Thread.sleep(sleepTime);  

            } catch (InterruptedException e) {  

                e.printStackTrace();  

            }  

            // 返回给调用者的值  

            String str = name + " sleep time:" + sleepTime;  

            System.out.println(name + " finished...");  

            return str;  

        }  

    }  

    private final int POOL_SIZE = 5;  

    private final int TOTAL_TASK = 20;  

    // 方法一,自己写集合来实现获取线程池中任务的返回结果  

    public void testByQueue() throws Exception {  

        // 创建线程池  

        ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);  

        BlockingQueue<Future<String>> queue = new LinkedBlockingQueue<Future<String>>();  

        // 向里面扔任务  

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

            Future<String> future = pool.submit(new MyThread("Thread" + i));  

            queue.add(future);  

        }  

        // 检查线程池任务执行结果  

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

            System.out.println("method1:" + queue.take().get());  

        }   

        // 关闭线程池  

        pool.shutdown();  

    }  

    // 方法二,通过CompletionService来实现获取线程池中任务的返回结果  

    public void testByCompetion() throws Exception {  

        // 创建线程池  

        ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);  

        CompletionService<String> cService = new ExecutorCompletionService<String>(pool);  

        // 向里面扔任务  

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

            cService.submit(new MyThread("Thread" + i));  

        }  
// 检查线程池任务执行结果 for (int i = 0; i < TOTAL_TASK; i++) { Future<String> future = cService.take(); System.out.println("method2:" + future.get()); } // 关闭线程池 pool.shutdown(); } public static void main(String[] args) throws Exception { ThreadPoolTest4 t = new ThreadPoolTest4(); t.testByQueue(); t.testByCompetion(); } }

部分输出:

...

 Thread4 finished...

method1:Thread4 sleep time:833

method1:Thread5 sleep time:158

Thread6 finished...

method1:Thread6 sleep time:826

method1:Thread7 sleep time:185

Thread9 finished...

Thread8 finished...

method1:Thread8 sleep time:929

method1:Thread9 sleep time:575

 ...

 Thread11 finished...

method2:Thread11 sleep time:952

Thread18 finished...

method2:Thread18 sleep time:793

Thread19 finished...

method2:Thread19 sleep time:763

Thread16 finished...

method2:Thread16 sleep time:990

 ...

3. 总结

使用方法一,自己创建一个集合来保存Future存根并循环调用其返回结果的时候,主线程并不能保证首先获得的是最先完成任务的线程返回值。它只是按加入线程池的顺序返回。因为take方法是阻塞方法,后面的任务完成了,前面的任务却没有完成,主程序就那样等待在那儿,只到前面的完成了,它才知道原来后面的也完成了。使用方法二,使用CompletionService来维护处理线程的返回结果时,主线程总是能够拿到最先完成的任务的返回值,而不管它们加入线程池的顺序。

CompletionService详解的更多相关文章

  1. Java并发之CompletionService详解

    CompletionService是什么? 它是JUC包中的一个接口类,默认实现类只有一个ExecutorCompletionService. CompletionService干什么的? 它将异步任 ...

  2. 跟着阿里p7一起学java高并发 - 第19天:JUC中的Executor框架详解1,全面掌握java并发核心技术

    这是java高并发系列第19篇文章. 本文主要内容 介绍Executor框架相关内容 介绍Executor 介绍ExecutorService 介绍线程池ThreadPoolExecutor及案例 介 ...

  3. java高并发系列 - 第20天:JUC中的Executor框架详解2之ExecutorCompletionService

    这是java高并发系列第20篇文章. 本文内容 ExecutorCompletionService出现的背景 介绍CompletionService接口及常用的方法 介绍ExecutorComplet ...

  4. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  5. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  6. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  7. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  8. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  9. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

随机推荐

  1. java restful response 万能类

    import java.util.HashMap; import java.util.Map; public class ResponseData { private final String mes ...

  2. 2017年总结&2018年计划

    谈一谈2017年计划: 1.完成壁咚项目2.写一个自己的扫描器3.完善web安全手册.4.搞一个大漏洞或CVE的漏洞 完成进度:1.壁咚这个项目,当初发誓要用java来写完,其实最开始就已经写完了,前 ...

  3. net.sf.fjep.fatjar_0.0.32 eclipse4.x 可以用的jar包

    http://pan.baidu.com/s/1nvlIw21?errno=0&errmsg=Auth%20Login%20Sucess&stoken=bb98db9f451c00ae ...

  4. Developer tools

    20. Developer tools Spring Boot includes an additional set of tools that can make the application de ...

  5. Node 中的 stream (流)

    流的概念 流(stream)在 Node.js 中是处理流数据的抽象接口(abstract interface). stream 模块提供了基础的 API .使用这些 API 可以很容易地来构建实现流 ...

  6. Spring 框架学习 有用

    1.1.1 spring的优势 方便解耦,简化开发 通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合.用户也不必再为单例模式类.属性文件 ...

  7. HDOJ 1097 A hard puzzle

    Problem Description lcy gives a hard puzzle to feng5166,lwg,JGShining and Ignatius: gave a and b,how ...

  8. 杭电acm 1076题

    水题,一个求闰年的题目,复习一下闰年的求法.... 1,如果能被4整除但不能被100整除的是闰年 2,能被400整除的是闰年 题目大意是:给定一个开始年份T以及一个正数N,要求求出从T开始,到了哪一年 ...

  9. python2 and python3 difference - division

    1. python2 2. python3 3.from python environment import py3 features

  10. 如何使用EditPlus将json格式字符串默认为UTF-8格式

    1.首先用EditPlus打开json格式的文件 2.选择Tools工具栏下的Configure  User Tools 3.点击左侧File 4.在右侧Default encoding中选择UTF- ...