Я только что нашел 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 любой результат, который можно получить, сделать это возвращение.