Когда я должен использовать CompletionService над ExecutorService?

Я только что нашел CompletionService в этом сообщении в блоге. Однако это действительно демонстрирует преимущества CompletionService над стандартным ExecutorService. Один и тот же код может быть написан с помощью. Итак, когда полезно использовать CompletionService?

Можете ли вы дать короткий образец кода, чтобы сделать его кристально чистым? Например, этот пример кода просто показывает, где не требуется 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();
    }

Ответ 1

С ExecutorService, после того как вы отправили задачи для запуска, вам нужно вручную написать код для эффективного получения результатов выполненных задач.

С CompletionService это в значительной степени автоматизировано. Разница не очень очевидна в представленном вами коде, потому что вы отправляете только одну задачу. Однако представьте, что у вас есть список задач для отправки. В приведенном ниже примере несколько задач передаются в CompletionService. Затем, вместо того, чтобы попытаться выяснить, какая задача выполнена (чтобы получить результаты), он просто просит экземпляр 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");
                }
            }
        }
    }

Ответ 2

Опуская много деталей:

  • ExecutorService = входящая очередь + рабочие потоки
  • CompletionService = входящая очередь + рабочие потоки + очередь вывода

Ответ 3

Я думаю, что javadoc лучше всего отвечает на вопрос о том, когда CompletionService полезен способом ExecutorService.

Служба, которая отделяет выпуск новых асинхронных задач от потребления результатов выполненных задач.

В принципе, этот интерфейс позволяет программе создавать производителей, которые создают и представляют задачи (и даже анализируют результаты этих представлений), не зная о каких-либо других потребителях результатов этих задач. Между тем, потребители, которые знают о CompletionService, могут poll для или take получить результаты, не зная о производителях, отправляющих задачи.

Для записи, и я могу ошибаться, потому что это довольно поздно, но я уверен, что образец кода в этом сообщении в блоге вызывает утечку памяти. Без активного пользователя, берущего результаты из внутренней очереди ExecutorCompletionService, я не уверен, как блоггер ожидал, что очередь будет стекать.

Ответ 4

В основном вы используете CompletionService, если хотите выполнить несколько задач параллельно, а затем работать с ними в порядке их завершения. Итак, если я выполняю 5 заданий, CompletionService даст мне первый, который заканчивается. Пример, где есть только одна задача, не дает дополнительного значения над Executor, кроме возможности отправить Callable.

Ответ 5

Прежде всего, если мы не хотим тратить процессорное время, мы не будем использовать

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

Мы должны использовать

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

Плохая вещь в этом коде заключается в том, что он отключит ExecutorService. Если мы хотим продолжить работу с ним (т.е. У нас есть какое-то рекурсивное создание задачи), у нас есть две альтернативы: invokeAll или ExecutorService.

invokeAll будет ждать, пока все задачи не будут завершены. ExecutorService предоставляет нам возможность поочередно брать результаты опроса.

И, конечно, рекурсивный пример:

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 :(
    }
}

Ответ 6

Посмотрите это самостоятельно во время выполнения, попробуйте реализовать оба решения (Executorservice и Completionservice), и вы увидите, как они себя ведут, и будет более понятным, когда использовать тот или иной. Здесь есть пример, если вы хотите http://rdafbn.blogspot.co.uk/2013/01/executorservice-vs-completionservice-vs.html

Ответ 7

Скажем, у вас есть 5 длинных задач (вызываемая задача), и вы передали эту задачу службе executer. Теперь представьте, что вы не хотите ждать, пока все 5 задач будут соревноваться, вместо этого вы хотите сделать какую-то обработку по этой задаче, если она будет завершена. Теперь это можно сделать либо путем написания логики опроса на будущих объектах, либо с помощью этого API.

Ответ 8

Если создатель задачи не заинтересован в результатах и ответственность за обработку результатов асинхронной задачи, выполняемой службой-исполнителем, лежит на другом компоненте, вам следует использовать CompletionService. Это поможет вам отделить обработчик результата задачи от производителя задачи. Смотрите пример http://www.zoftino.com/java-concurrency-executors-framework-tutorial

Ответ 9

есть еще одно преимущество использования сервиса завершения: производительность

когда вы вызываете future.get(), вы ожидаете вращения:

от 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;
            }

когда у вас есть долгосрочное задание, это будет катастрофой для производительности.

В случае завершения обслуживания, когда задача будет выполнена, ее результат будет поставлен в очередь, и вы сможете опрашивать очередь с более низкой производительностью.

завершение обслуживания достичь этого с помощью задачи обтекания с 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;
}

Ответ 10

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;
    }
}

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

разница в журналах для службы завершения исполнителя: текущий поток - пул-1-нить-1, результат должен быть равен 1, текущий поток - пул-1-нить-2, результат должен быть равен 2, текущий поток - пул-1-нить- 3 результат должен быть 3 текущий поток - пул-1-нить-4 результат должен быть 4 текущий поток - пул-1-нить-6 результат должен быть 6 текущий поток - пул-1-нить-5 результат должен быть 5 текущим потоком является pool-1-thread-7 результат должен быть 7 текущим потоком является pool-1-thread-9 результат должен быть 9 текущим потоком является pool-1-thread-8 результат должен Если 8 - задача выполнена для пула-1-нить-9, то результат должен быть 9, а результат - 9 - Задача выполнена для пула-1-нить-8. Результат должен быть 8 - Задача выполнена для пула-1-нить-7. результат должен быть 7 задача выполнена для пула-1-поток-6 результат должен быть 6 задача выполнена для пула-1-поток-5 результат должен быть 5 задача выполнена для пула-1-поток-4 результат должен быть 4 задача выполнена для пула-1-нить-3 результат должен быть 3

задача выполнена для пула-1-нить-2 результат должен быть 2

текущий поток - пул-1-нить-1, результат должен быть равен 1, текущий поток - пул-1-нить-3, результат должен быть 3, текущий поток - пул-1-нить-2, результат должен быть равен 2 текущий thread это 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 задача выполнена для pool- 1-thread-9 результат должен быть 9 задача выполнена для pool-1-thread-8 результат должен быть 8 задача завершена для pool-1-thread-7 результат должен быть 7 задача выполнена для pool-1- нить-6 результат должен быть 6 задача выполнена для пула-1-нить-5 результат должен быть 5 задача выполнена для пула-1-нить-4 результат должен быть 4 задача выполнена для пула-1-нить- 3 результат должен быть 3 задача выполнена для ol-1-thread-2 результат должен быть 2 задача выполнена для pool-1-thread-1 результат должен быть 1 результат будущем равен 1

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

для executorservice результат будет доступен после выполнения всех задач.

executor completeservice любой результат, который можно получить, сделать это возвращение.