简体   繁体   English

我什么时候应该在 ExecutorService 上使用 CompletionService?

[英]When should I use a CompletionService over an ExecutorService?

I just found CompletionService in this blog post .我刚刚在这篇博文中找到了 CompletionService。 However, this does't really showcases the advantages of CompletionService over a standard ExecutorService.但是,这并没有真正展示 CompletionService 相对于标准 ExecutorService 的优势。 The same code can be written with either.可以使用任何一种编写相同的代码。 So, when is a CompletionService useful?那么,什么时候 CompletionService 有用呢?

Can you give a short code sample to make it crystal clear?您能否提供一个简短的代码示例以使其清晰明了? For example, this code sample just shows where a CompletionService is not needed (=equivalent to ExecutorService)例如,此代码示例仅显示不需要 CompletionService 的位置(= 相当于 ExecutorService)

    ExecutorService taskExecutor = Executors.newCachedThreadPool();
    //        CompletionService<Long> taskCompletionService =
    //                new ExecutorCompletionService<Long>(taskExecutor);
    Callable<Long> callable = new Callable<Long>() {
        @Override
        public Long call() throws Exception {
            return 1L;
        }
    };

    Future<Long> future = // taskCompletionService.submit(callable);
        taskExecutor.submit(callable);

    while (!future.isDone()) {
        // Do some work...
        System.out.println("Working on something...");
    }
    try {
        System.out.println(future.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

Omitting many details: 省略许多细节:

  • ExecutorService = incoming queue + worker threads ExecutorService =传入队列+工作线程
  • CompletionService = incoming queue + worker threads + output queue CompletionService =传入队列+工作线程+输出队列

With ExecutorService , once you have submitted the tasks to run, you need to manually code for efficiently getting the results of the tasks completed. 使用ExecutorService ,一旦提交了要运行的任务,就需要手动编写代码以有效地获取已完成任务的结果。

With CompletionService , this is pretty much automated. 使用CompletionService ,这几乎是自动化的。 The difference is not very evident in the code you have presented because you are submitting just one task. 由于您只提交了一项任务,因此您提供的代码中的差异不是很明显。 However, imagine you have a list of tasks to be submitted. 但是,假设您有一个要提交的任务列表。 In the example below, multiple tasks are submitted to the CompletionService. 在下面的示例中,将多个任务提交给CompletionService。 Then, instead of trying to find out which task has completed (to get the results), it just asks the CompletionService instance to return the results as they become available. 然后,它不是试图找出已完成的任务(以获得结果),而是要求CompletionService实例在结果可用时返回结果。

public class CompletionServiceTest {

        class CalcResult {
             long result ;

             CalcResult(long l) {
                 result = l;
             }
        }

        class CallableTask implements Callable<CalcResult> {
            String taskName ;
            long  input1 ;
            int input2 ;

            CallableTask(String name , long v1 , int v2 ) {
                taskName = name;
                input1 = v1;
                input2 = v2 ;
            }

            public CalcResult call() throws Exception {
                System.out.println(" Task " + taskName + " Started -----");
                for(int i=0;i<input2 ;i++) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        System.out.println(" Task " + taskName + " Interrupted !! ");
                        e.printStackTrace();
                    }
                    input1 += i;
                }
                System.out.println(" Task " + taskName + " Completed @@@@@@");
                return new CalcResult(input1) ;
            }

        }

        public void test(){
            ExecutorService taskExecutor = Executors.newFixedThreadPool(3);
            CompletionService<CalcResult> taskCompletionService = new ExecutorCompletionService<CalcResult>(taskExecutor);

            int submittedTasks = 5;
            for (int i=0;i< submittedTasks;i++) {
                taskCompletionService.submit(new CallableTask (
                        String.valueOf(i), 
                            (i * 10), 
                            ((i * 10) + 10  )
                        ));
               System.out.println("Task " + String.valueOf(i) + "subitted");
            }
            for (int tasksHandled=0;tasksHandled<submittedTasks;tasksHandled++) {
                try {
                    System.out.println("trying to take from Completion service");
                    Future<CalcResult> result = taskCompletionService.take();
                    System.out.println("result for a task availble in queue.Trying to get()");
                    // above call blocks till atleast one task is completed and results availble for it
                    // but we dont have to worry which one

                    // process the result here by doing result.get()
                    CalcResult l = result.get();
                    System.out.println("Task " + String.valueOf(tasksHandled) + "Completed - results obtained : " + String.valueOf(l.result));

                } catch (InterruptedException e) {
                    // Something went wrong with a task submitted
                    System.out.println("Error Interrupted exception");
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    // Something went wrong with the result
                    e.printStackTrace();
                    System.out.println("Error get() threw exception");
                }
            }
        }
    }

I think the javadoc best answers the question of when the CompletionService is useful in a way an ExecutorService isn't. 我认为javadoc最好的答案是什么时候CompletionServiceExecutorService不是这样的方式中有用。

A service that decouples the production of new asynchronous tasks from the consumption of the results of completed tasks. 一种服务,它将新异步任务的生成与已完成任务的结果消耗分离。

Basically, this interface allows a program to have producers which create and submit tasks (and even examine the results of those submissions) without knowing about any other consumers of the results of those tasks. 基本上,这个接口允许程序让生产者创建和提交任务(甚至检查这些提交的结果),而不知道这些任务结果的任何其他消费者。 Meanwhile, consumers which are aware of the CompletionService could poll for or take results without being aware of the producers submitting the tasks. 同时,了解CompletionService消费者可以在不知道生产者提交任务的情况下进行polltake结果。

For the record, and I could be wrong because it is rather late, but I am fairly certain that the sample code in that blog post causes a memory leak. 为了记录,我可能是错的,因为它已经很晚了,但我相当确定该博客文章中的示例代码会导致内存泄漏。 Without an active consumer taking results out of the ExecutorCompletionService 's internal queue, I'm not sure how the blogger expected that queue to drain. 如果没有活跃的消费者从ExecutorCompletionService的内部队列中获取结果,我不确定博主是如何期望排队的。

Basically you use a CompletionService if you want to execute multiple tasks in parallel and then work with them in their completion order. 基本上,如果要并行执行多个任务,然后在完成顺序中使用它们,则使用CompletionService So, if I execute 5 jobs, the CompletionService will give me the first one that that finishes. 因此,如果我执行5个作业, CompletionService将为我提供第一个完成的作业。 The example where there is only a single task confers no extra value over an Executor apart from the ability to submit a Callable . 除了提交Callable的能力之外,只有一个任务的示例不会给Executor带来额外的价值。

First of all, if we do not want to waste processor time, we will not use 首先,如果我们不想浪费处理器时间,我们将不会使用

while (!future.isDone()) {
        // Do some work...
}

We must use 我们必须使用

service.shutdown();
service.awaitTermination(14, TimeUnit.DAYS);

The bad thing about this code is that it will shut down ExecutorService . 这段代码的坏处是它将关闭ExecutorService If we want to continue work with it (ie we have some recursicve task creation), we have two alternatives: invokeAll or ExecutorService . 如果我们想继续使用它(即我们有一些recursicve任务创建),我们有两个选择:invokeAll或ExecutorService

invokeAll will wait untill all tasks will be complete. invokeAll将等待,直到所有任务完成。 ExecutorService grants us ability to take or poll results one by one. ExecutorService授予我们逐个获取或轮询结果的能力。

And, finily, recursive example: 最后,递归示例:

ExecutorService executorService = Executors.newFixedThreadPool(THREAD_NUMBER);
ExecutorCompletionService<String> completionService = new ExecutorCompletionService<String>(executorService);

while (Tasks.size() > 0) {
    for (final Task task : Tasks) {
        completionService.submit(new Callable<String>() {   
            @Override
            public String call() throws Exception {
                return DoTask(task);
            }
        });
    } 

    try {                   
        int taskNum = Tasks.size();
        Tasks.clear();
        for (int i = 0; i < taskNum; ++i) {
            Result result = completionService.take().get();
            if (result != null)
                Tasks.add(result.toTask());
        }           
    } catch (InterruptedException e) {
    //  error :(
    } catch (ExecutionException e) {
    //  error :(
    }
}

See it by yourself at run time,try to implement both solutions (Executorservice and Completionservice) and you'll see how different they behave and it will be more clear on when to use one or the other. 在运行时自己查看,尝试实现两个解决方案(Executorservice和Completionservice),您将看到它们的行为有多么不同,并且更清楚何时使用其中一个。 There is an example here if you want http://rdafbn.blogspot.co.uk/2013/01/executorservice-vs-completionservice-vs.html 这里有一个例子,如果你想要http://rdafbn.blogspot.co.uk/2013/01/executorservice-vs-completionservice-vs.html

Let's say you have 5 long running task(callable task) and you have submitted those task to executer service. 假设您有5个长时间运行的任务(可调用任务),并且您已将这些任务提交给执行者服务。 Now imagine you don't want to wait for all 5 task to compete instead you want to do some sort of processing on these task if any one completes. 现在假设您不想等待所有5个任务竞争,而是想要对这些任务进行某种处理(如果任何一个完成)。 Now this can be done either by writing polling logic on future objects or use this API. 现在可以通过在未来对象上编写轮询逻辑或使用此API来完成此操作。

there is another advantage of using completionservice: Performance 使用completionservice有另一个好处: 性能

when you call future.get() , you are spin waiting: 当你调用future.get() ,你正在旋转等待:

from java.util.concurrent.CompletableFuture 来自java.util.concurrent.CompletableFuture

  private Object waitingGet(boolean interruptible) {
        Signaller q = null;
        boolean queued = false;
        int spins = -1;
        Object r;
        while ((r = result) == null) {
            if (spins < 0)
                spins = (Runtime.getRuntime().availableProcessors() > 1) ?
                    1 << 8 : 0; // Use brief spin-wait on multiprocessors
            else if (spins > 0) {
                if (ThreadLocalRandom.nextSecondarySeed() >= 0)
                    --spins;
            }

when you have a long-running task, this will be a disaster for performance. 当你有一个长期运行的任务时,这将是性能的灾难。

with completionservice, once the task is done, it's result will be enqueued and you can poll the queue with lower performance overhand. 使用completionservice,一旦任务完成,它的结果将被排队,你可以用较低的性能轮询队列。

completionservice achieve this by using wrap task with a done hook. completionservice通过使用带有done钩子的wrap任务来实现这done

java.util.concurrent.ExecutorCompletionService

    private class QueueingFuture extends FutureTask<Void> {
    QueueingFuture(RunnableFuture<V> task) {
        super(task, null);
        this.task = task;
    }
    protected void done() { completionQueue.add(task); }
    private final Future<V> task;
}

If the task producer is not interested in the results and it is another component's responsibility to process results of asynchronous task executed by executor service, then you should use CompletionService. 如果任务生成者对结果不感兴趣并且处理执行程序服务所执行的异步任务的结果是另一个组件的责任,那么您应该使用CompletionService。 It helps you in separating task result processor from task producer. 它可以帮助您将任务结果处理器与任务生成器分离。 See example http://www.zoftino.com/java-concurrency-executors-framework-tutorial 请参见示例http://www.zoftino.com/java-concurrency-executors-framework-tutorial

package com.barcap.test.test00;

import java.util.concurrent.*;

/**
 * Created by Sony on 25-04-2019.
 */
public class ExecutorCompletest00 {

    public static void main(String[] args) {

        ExecutorService exc= Executors.newFixedThreadPool( 10 );
        ExecutorCompletionService executorCompletionService= new ExecutorCompletionService( exc );

        for (int i=1;i<10;i++){
            Task00 task00= new Task00( i );
            executorCompletionService.submit( task00 );
        }
        for (int i=1;i<20;i++){
            try {
                Future<Integer> future= (Future <Integer>) executorCompletionService.take();
                Integer inttest=future.get();
                System.out.println(" the result of completion service is "+inttest);

               break;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

======================================================= ================================================== =====

package com.barcap.test.test00;

import java.util.*;
import java.util.concurrent.*;

/**
 * Created by Sony on 25-04-2019.
 */
public class ExecutorServ00 {

    public static void main(String[] args) {
        ExecutorService executorService=Executors.newFixedThreadPool( 9 );
        List<Future> futList= new ArrayList <>(  );
        for (int i=1;i<10;i++) {
           Future result= executorService.submit( new Task00( i ) );
           futList.add( result );
        }

         for (Future<Integer> futureEach :futList ){
             try {
              Integer inm=   futureEach.get();

                 System.out.println("the result of future executorservice is "+inm);
                 break;
             } catch (InterruptedException e) {
                 e.printStackTrace();
             } catch (ExecutionException e) {
                 e.printStackTrace();
             }
         }
    }
}

=========================================================== ================================================== =========

package com.barcap.test.test00;

import java.util.concurrent.*;

/**
 * Created by Sony on 25-04-2019.
 */
public class Task00 implements Callable<Integer> {

    int i;

    public Task00(int i) {
        this.i = i;
    }

    @Override
    public Integer call() throws Exception {
        System.out.println(" the current thread is "+Thread.currentThread().getName()  +" the result should be "+i);
        int sleepforsec=100000/i;
         Thread.sleep( sleepforsec );
        System.out.println(" the task complted for "+Thread.currentThread().getName()  +" the result should be "+i);



        return i;
    }
}

====================================================================== ================================================== ====================

difference of logs for executor completion service: the current thread is pool-1-thread-1 the result should be 1 the current thread is pool-1-thread-2 the result should be 2 the current thread is pool-1-thread-3 the result should be 3 the current thread is pool-1-thread-4 the result should be 4 the current thread is pool-1-thread-6 the result should be 6 the current thread is pool-1-thread-5 the result should be 5 the current thread is pool-1-thread-7 the result should be 7 the current thread is pool-1-thread-9 the result should be 9 the current thread is pool-1-thread-8 the result should be 8 the task complted for pool-1-thread-9 the result should be 9 teh result is 9 the task complted for pool-1-thread-8 the result should be 8 the task complted for pool-1-thread-7 the result should be 7 the task complted for pool-1-thread-6 the result should be 6 the task complted for pool-1-thread-5 the result should be 5 the task complted for pool-1-thread-4 the result should be 4 the task complted 执行者完成服务的日志差异:当前线程是pool-1-thread-1,结果应该是1当前线程是pool-1-thread-2结果应该是2当前线程是pool-1-thread- 3结果应该是3当前线程是pool-1-thread-4结果应该是4当前线程是pool-1-thread-6结果应该是6当前线程是pool-1-thread-5结果应该是5当前线程是pool-1-thread-7结果应该是7当前线程是pool-1-thread-9结果应该是9当前线程是pool-1-thread-8结果应该8为任务complted为pool-1-thread-9结果应该是9 teh结果是9任务complted为pool-1-thread-8结果应该是8任务complted为pool-1-thread-7结果应该是7为pool-1-thread-6编译的任务结果应该是6为pool-1-thread-5编译的任务结果应该是5为pool-1-thread-4编译的任务结果应该是4完成任务 for pool-1-thread-3 the result should be 3 对于pool-1-thread-3,结果应为3

the task complted for pool-1-thread-2 the result should be 2 为pool-1-thread-2编译的任务结果应为2

the current thread is pool-1-thread-1 the result should be 1 the current thread is pool-1-thread-3 the result should be 3 the current thread is pool-1-thread-2 the result should be 2 the current thread is pool-1-thread-5 the result should be 5 the current thread is pool-1-thread-4 the result should be 4 the current thread is pool-1-thread-6 the result should be 6 the current thread is pool-1-thread-7 the result should be 7 the current thread is pool-1-thread-8 the result should be 8 the current thread is pool-1-thread-9 the result should be 9 the task complted for pool-1-thread-9 the result should be 9 the task complted for pool-1-thread-8 the result should be 8 the task complted for pool-1-thread-7 the result should be 7 the task complted for pool-1-thread-6 the result should be 6 the task complted for pool-1-thread-5 the result should be 5 the task complted for pool-1-thread-4 the result should be 4 the task complted for pool-1-thread-3 the result should be 3 the task complted for po 当前线程是pool-1-thread-1结果应该是1当前线程是pool-1-thread-3结果应该是3当前线程是pool-1-thread-2结果应该是2当前线程是pool-1-thread-5结果应该是5当前线程是pool-1-thread-4结果应该是4当前线程是pool-1-thread-6结果应该是6当前线程是pool-1-thread-7结果应该是7当前线程是pool-1-thread-8结果应该是8当前线程是pool-1-thread-9结果应该是9任务complted for pool- 1-thread-9结果应该是9为pool-1-thread-8完成的任务结果应该是8为pool-1-thread-7编译的任务结果应该是7为pool-1-完成的任务thread-6结果应该是6为pool-1-thread-5编译的任务结果应该是5为pool-1-thread-4编译的任务结果应该是4为pool-1-thread- complted的任务3结果应该是3完成po的任务 ol-1-thread-2 the result should be 2 the task complted for pool-1-thread-1 the result should be 1 the result of future is 1 ol-1-thread-2结果应该是2为pool-1-thread-1完成的任务结果应该是1,未来的结果是1

======================================================= ================================================== =====

for executorservice the result will only be avialable after all tasks complted. 对于executorservice,只有在完成所有任务后才能获得结果。

executor completionservice any result avilable make that return. 执行者完成服务任何结果avilable使返回。

assuming you execute a tasks in parallel and you save the Future results in a list:假设您并行执行任务并将 Future 结果保存在列表中:

The practical main difference between ExecutorService and CompletionService is: ExecutorService 和 CompletionService 之间的实际主要区别是:

ExecutorService get() will try to retrieve the results in the submitted order waiting for completion. ExecutorService get() 将尝试在提交的订单中检索结果等待完成。

CompletionService take() + get() will try to retrieve the results in the completion order disregarding the submission order. CompletionService take() + get() 将尝试以完成顺序检索结果,而不考虑提交顺序。

ExecutorCompletionService class implements CompletionService . ExecutorCompletionService class 实现CompletionService ExecutorCompletionService returns futures objects based on completion order, so whichever task executes first, will be returned first. ExecutorCompletionService根据完成顺序返回 futures 对象,因此无论哪个任务先执行,都会先返回。 You just need to call executorCompletionService.take() to get completed Future object.您只需调用executorCompletionService.take()即可完成 Future object。

I found a blog that clear my thought.我找到了一个让我思路清晰的博客。

java2blog link with example 带有示例的 java2blog 链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我什么时候应该使用 Executor 而不是 ExecutorService - When should I use Executor over ExecutorService 我应该使用ExecutorService吗? - Should I use ExecutorService? 我什么时候应该在 LinkedBlockingQueue 上使用 SynchronousQueue - When should I use SynchronousQueue over LinkedBlockingQueue Java ExecutorService:我应该先使用lock来使用execute吗? - Java ExecutorService: should I put a lock before to use execute? 使用CompletionService时执行executorService.awaitTermination - Enforce executorService.awaitTermination while using CompletionService 什么时候我应该使用懒惰的单身人士而不是普通的单身人士? - When should I use lazy Singletons over normal Singletons? 我什么时候应该在 PriorityQueue 上使用 TreeMap,反之亦然? - When should I use a TreeMap over a PriorityQueue and vice versa? 我应该叫`executorService.shutdown`和`executorService.awaitTermination`吗? - should I call `executorService.shutdown` and `executorService.awaitTermination`? 当executorService.shutdown(); 应该叫 - When the executorService.shutdown(); should be called 如何在不询问Future值的情况下使ExecutorService或CompletionService完成? - How to get ExecutorService or CompletionService to finish without asking for values of Futures?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM