Java多线程-CompletionService

原创 2017年05月12日 16:03:15

原文地址 http://blog.csdn.net/qq_25806863/article/details/71743659

之前说过,线程池ThreadPoolExecutor可以调用submit方法来获取返回值Future。像下面这样:

这里先定义三个Callable,之后都用这三个:

        Callable callable1 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(5000);
                return "我是call1的返回值";
            }
        };
        Callable callable2 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3000);
                return "我是call2的返回值";
            }
        };
        Callable callable3 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(1000);
                return "我是call3的返回值";
            }
        };

直接使用ThreadPoolExecutor的submit获取结果的使用方法是这样的:

        //声明一个线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);
        //提交三个任务
        Future future1 = executor.submit(callable1);
        Future future2 = executor.submit(callable2);
        Future future2 = executor.submit(callable2);
        //开始获取返回值
        System.out.println("两个任务提交完毕,开始获取结果 "+getStringDate());
        System.out.println(future1.get()+" "+getStringDate());
        System.out.println(future2.get()+" "+getStringDate());
        System.out.println(future3.get()+" "+getStringDate());
        System.out.println("获取结果完毕 "+getStringDate());

根据之前的理解,get()方法是有阻塞性的,因为future1的任务执行时间是5秒,所以在future1.get()这行代码上会阻塞5秒,然后才会获取到结果,继续往下执行。而在5秒内future2和future3的任务已经执行完了,所以会立马得到结果。

真实输出也是这样:

明明future2和future3的任务早就执行完了,却被future1.get()方法阻塞了。

使用CompletionService可以作为一种解决方法。

CompletionService简介

CompletionService的主要功能就是一边生成任务,一边获取任务的返回值。让两件事分开执行,任务之间不会互相阻塞。

CompletionService在提交任务之后,会根据任务完成顺序来获取返回值,也就是谁先完成就返回谁的返回值。

CompletionService是一个接口:

public interface CompletionService<V> {
    Future<V> submit(Callable<V> var1);
    Future<V> submit(Runnable var1, V var2);
    Future<V> take() throws InterruptedException;
    Future<V> poll();
    Future<V> poll(long var1, TimeUnit var3) throws InterruptedException;
}

CompletionService只有一个实现类,就是ExecutorCompletionService

我这里有两个是因为用的AndroidStudio,一个是java的SDK的一个是Android的SDK的。

ExecutorCompletionService的使用

CompletionService接口一共也就定义了那么几个方法,submit方法和ExecutorService的submit没什么不同。

下面主要分析一下take()方法和poll()方法

构造方法

ExecutorCompletionService的构造方法有两个:

public ExecutorCompletionService(Executor var1)
ExecutorCompletionService(Executor var1, BlockingQueue<Future<V>> var2)

由此可见,CompletionService对任务的各种操作还是通过Executor来实现的,一般就是ThreadPoolExecutor。

下面是一个简单例子:

还是用一开始的三个Callable,这次用CompletionService来提交任务并获取结果。

//新建一个线程池executor
ExecutorService executor = Executors.newFixedThreadPool(5);
//用线程池executor新建一个CompletionService
CompletionService completionService = new ExecutorCompletionService(executor);
//用CompletionService提交任务
completionService.submit(callable1);
completionService.submit(callable2);
completionService.submit(callable3);
//用CompletionService获取结果
System.out.println("两个任务提交完毕,开始获取结果 "+getStringDate());
System.out.println(completionService.take().get()+" "+getStringDate());
System.out.println(completionService.take().get()+" "+getStringDate());
System.out.println(completionService.take().get()+" "+getStringDate());
System.out.println("获取结果完毕 "+getStringDate());

可以看下输出:

虽然提交的顺序是1,2,3,但是获取结果的时候是按任务完成顺序来获取的,所以结果是3,2,1.

take()方法

其实take()方法也是一个阻塞方法,调用这个方法时,他会一直等待直到线程池中返回一个结果,哪个任务先完成,就返回哪个任务的结果。

在上面的例子中,由于callable3是最先完成的,所以最先拿到的值就是callable3的返回值。

因为刚好提交了3个任务,调用了3次take()方法,因此刚好能拿到全部的任务的结果。

如果在调用一次take()方法,那么就会因为等不到有任务返回结果而阻塞在那里:

例如值提交一个任务,而调用两次take()方法,那么程序就会阻塞在第二个take()方法那里等待一个结果

        ExecutorService executor = Executors.newFixedThreadPool(5);
        CompletionService completionService = new ExecutorCompletionService(executor);
        completionService.submit(callable1);
        System.out.println("两个任务提交完毕,开始获取结果 "+getStringDate());

        System.out.println(completionService.take().get()+" "+getStringDate());
        System.out.println(completionService.take().get()+" "+getStringDate());

        System.out.println("获取结果完毕 "+getStringDate());

结果会一直是这样

poll()方法和poll(long var1, TimeUnit var3)方法

Poll()方法也是获取返回值,使用方法也跟take()一样。

而poll()方法和take()方法的区别就是,poll()方法不会阻塞的去等结果,而是如果调用poll()方法的时候没有结果可以获取就直接返回一个null,然后程序继续往下运行。

这时如果调用poll().get()可能会引发空指针异常java.lang.NullPointerException

例子:

依旧是一开始那三个任务,在循环中连续调用8次poll()方法,每次间隔1秒钟:

ExecutorService executor = Executors.newFixedThreadPool(5);
CompletionService completionService = new ExecutorCompletionService(executor);
completionService.submit(callable1);
completionService.submit(callable2);
completionService.submit(callable3);
System.out.println("两个任务提交完毕,开始获取结果 "+getStringDate());
for (int i = 0; i < 8; i++) {
              Future future = completionService.poll();
              if (future!=null){
              //如果future为空,会引发 NullPointerException
                  System.out.println(future.get() + getStringDate());
              }else {
                  System.out.println(future+" "+getStringDate());
              }
              Thread.sleep(1000);
          }          
System.out.println("获取结果完毕 "+getStringDate());

输出:

每次调用都是立马返回,毫不犹豫。所以没有结果的时候就返回空。

而poll(long var1, TimeUnit var3)方法就相当于给他强制设置了一个等待时间,你如果拿不到结果就等这么久,等这么久还拿不到再返回null。

把上面的循环改成这样:

for (int i = 0; i < 8; i++) {
    Future future = completionService.poll(1, TimeUnit.SECONDS);
    if (future!=null){
        System.out.println(future.get() + getStringDate());
    }else {
        System.out.println(future+" "+getStringDate());
    }
}

不在睡眠了,每次调用poll()方法个体1秒的等待时间。

这里第一次调用就等了1秒,然后在1秒内等到了call3的返回值,就返回call3的返回值。

第二次循环又等了一秒,一秒内没有获得结果,返回null。

Java:多线程,线程池,使用CompletionService通过Future来处理Callable的返回结果

1. 背景 在Java5的多线程中,可以使用Callable接口来实现具有返回值的线程。使用线程池的submit方法提交Callable任务,利用submit方法返回的Future存根,调用此存...

Java:多线程,线程池,使用CompletionService通过Future来处理Callable的返回结果

1. 背景 在Java5的多线程中,可以使用Callable接口来实现具有返回值的线程。使用线程池的submit方法提交Callable任务,利用submit方法返回的Future存根,调用此存...

java中的多线程包---CompletionService Executor + BlockingQueue

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/27250059 一般情况下,我们使用Runnable作为基本的任务表示形式,...

多线程-CompletionService

转载地址:  http://langgufu.iteye.com/blog/2102549   这个类就相当于打包了一个系列的Future,然后把这个写结果放到一个集合中。 同时,放入...

多线程获取线程返回值---Future And CompletionService

在某些应用场景下,我们需要获取线程的返回值,采用传统方式或许比较麻烦,那么jdk5为我们封装好了Future和CompletionService。     Future 表示异步计算的结果。它提供了...

java高并发之CompletionService优化多线程并发

1:还是上个需求 工作流策略的一个节点,需要查询13个第三方的数据,并返回查询结果变量,由于时间的要求,必须实现并行查询,而且要求13个查询全部完成才能返回工作流节点。 没看过上个帖子的可以看...

多线程并发执行任务,取结果归集。终极总结:Future、FutureTask、CompletionService、CompletableFuture

http://www.cnblogs.com/dennyzhangdd/p/7010972.html 目录 1.Futrue 2.FutureTask 3....

java socket多线程通信案例

  • 2017年12月06日 15:43
  • 4KB
  • 下载

java多线程查询数据库

  • 2017年12月04日 13:38
  • 2KB
  • 下载

Java多线程中的wait和notifyAll

想要直接看代码的可以跳过下面我对并发的简单理解,直接看代码(我已经加了注释,代码写的有问题还请诸位大神指出,以免误人子弟)。         最近要用到Java多线程,才好好的看了一下多线程。谁知道这...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java多线程-CompletionService
举报原因:
原因补充:

(最多只允许输入30个字)