Ich habe gerade CompletionService in diesem Blog-Beitrag gefunden . Dies zeigt jedoch nicht wirklich die Vorteile von CompletionService gegenüber einem Standard-ExecutorService. Der gleiche Code kann mit beiden geschrieben werden. Wann ist ein CompletionService nützlich?
Können Sie ein kurzes Codebeispiel geben, um es kristallklar zu machen? Dieses Codebeispiel zeigt beispielsweise nur, wo ein CompletionService nicht benötigt wird (= entspricht 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();
}
quelle
take
undpoll
bei CompletionService sind threadsicher? In Ihrem Beispiel werden Aufgaben beim ersten Aufruf noch ausgeführttake()
, und ich sehe keine explizite Synchronisierung.take()
ist in der Tat threadsicher. Sie können die JavaDocs einlesen,take()
warten jedoch grundsätzlich auf das nächste abgeschlossene Ergebnis und geben es dann zurück. DasCompletionService
funktioniert mit einemBlockingQueue
für die Ausgabe.take()
wenn keine Ergebnisse mehr vorliegen, wartet dieser Thread auf unbestimmte Zeit. Keine Ausnahmen. Sie müssen Ihre Logik entwerfen, um diese Situation zu erfassen und das Warten zu beenden. Dies sollte nicht schwierig sein - normalerweise können Sie feststellen, dass alle Ihre Aufgaben erledigt sind, ohne dass der CompletionService Ihnen dies mitteilt.Viele Details weglassen:
quelle
Grundsätzlich verwenden Sie a,
CompletionService
wenn Sie mehrere Aufgaben parallel ausführen und dann in ihrer Abschlussreihenfolge mit ihnen arbeiten möchten. Wenn ich also 5 Jobs ausführe,CompletionService
gibt mir der den ersten, der beendet ist. Das Beispiel, in dem es nur eine einzige Aufgabe gibt, verleihtExecutor
außer der Möglichkeit, eine Aufgabe einzureichen, keinen zusätzlichen WertCallable
.quelle
Ich denke, der Javadoc beantwortet am besten die Frage, wann das
CompletionService
in einer Weise nützlichExecutorService
ist und nicht.Grundsätzlich ermöglicht diese Schnittstelle einem Programm, Produzenten zu haben, die Aufgaben erstellen und einreichen (und sogar die Ergebnisse dieser Einreichungen untersuchen), ohne über andere Verbraucher der Ergebnisse dieser Aufgaben Bescheid zu wissen. In der Zwischenzeit Verbraucher, die sich der
CompletionService
Möglichkeitpoll
oder dertake
Ergebnisse bewusst sind, ohne zu wissen, dass die Hersteller die Aufgaben einreichen.Für die Aufzeichnung, und ich könnte mich irren, weil es ziemlich spät ist, aber ich bin ziemlich sicher, dass der Beispielcode in diesem Blog-Beitrag einen Speicherverlust verursacht. Ohne einen aktiven Verbraucher, der Ergebnisse aus der
ExecutorCompletionService
internen Warteschlange des Benutzers entnimmt, bin ich mir nicht sicher, wie der Blogger damit gerechnet hat, dass diese Warteschlange leer wird.quelle
Erstens, wenn wir keine Prozessorzeit verschwenden wollen, werden wir nicht verwenden
while (!future.isDone()) { // Do some work... }
Wir müssen verwenden
service.shutdown(); service.awaitTermination(14, TimeUnit.DAYS);
Das Schlechte an diesem Code ist, dass er heruntergefahren wird
ExecutorService
. Wenn wir damit weiterarbeiten möchten (dh wir haben eine rekursive Aufgabenerstellung), haben wir zwei Alternativen: invokeAll oderExecutorService
.invokeAll
wartet, bis alle Aufgaben abgeschlossen sind.ExecutorService
gewährt uns die Möglichkeit, Ergebnisse einzeln zu erfassen oder abzufragen.Und schließlich ein rekursives Beispiel:
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 :( } }
quelle
Überzeugen Sie sich selbst zur Laufzeit, versuchen Sie, beide Lösungen (Executorservice und Completionservice) zu implementieren, und Sie werden sehen, wie unterschiedlich sie sich verhalten, und es wird klarer, wann die eine oder die andere verwendet werden soll. Hier finden Sie ein Beispiel, wenn Sie http://rdafbn.blogspot.co.uk/2013/01/executorservice-vs-completionservice-vs.html möchten
quelle
Angenommen, Sie haben 5 Aufgaben mit langer Laufzeit (aufrufbare Aufgabe) und haben diese Aufgabe an den Executer Service gesendet. Stellen Sie sich nun vor, Sie möchten nicht darauf warten, dass alle 5 Aufgaben miteinander konkurrieren, sondern eine Art Verarbeitung für diese Aufgabe durchführen, wenn eine abgeschlossen ist. Dies kann jetzt entweder durch Schreiben einer Abfragelogik für zukünftige Objekte oder durch Verwenden dieser API erfolgen.
quelle
Wenn der Taskproduzent nicht an den Ergebnissen interessiert ist und es in der Verantwortung einer anderen Komponente liegt, die Ergebnisse der vom Executor-Service ausgeführten asynchronen Task zu verarbeiten, sollten Sie CompletionService verwenden. Es hilft Ihnen bei der Trennung des Task-Ergebnisprozessors vom Task-Produzenten. Siehe Beispiel http://www.zoftino.com/java-concurrency-executors-framework-tutorial
quelle
Die Verwendung von Completionservice bietet noch einen weiteren Vorteil: Leistung
Wenn Sie anrufen
future.get()
, warten Sie:von
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; }
Wenn Sie eine lange laufende Aufgabe haben, ist dies eine Leistungskatastrophe.
Wenn der Abschlussdienst abgeschlossen ist, wird das Ergebnis in die Warteschlange gestellt, und Sie können die Warteschlange mit geringerer Leistung überhand abfragen.
Vervollständigungsservice erreichen dies durch die Verwendung einer Wrap-Task mit einem
done
Hook.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; }
quelle
poll
Methode implementiert hat , gibt es keinen Grund zu der Annahme, dass sie einen „geringeren Leistungsaufwand“ hatte.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; } }
================================================== ====================
Unterschied der Protokolle für den Executor Completion Service:Der aktuelle Thread ist Pool-1-Thread-1. Das Ergebnis sollte 1 sein. Der aktuelle Thread ist Pool-1-Thread-2. Das Ergebnis sollte 2 sein. Der aktuelle Thread ist Pool-1-Thread-3. Das Ergebnis sollte 3 sein Thread ist Pool-1-Thread-4 Das Ergebnis sollte 4 sein. Der aktuelle Thread ist Pool-1-Thread-6. Das Ergebnis sollte 6 sein. Der aktuelle Thread ist Pool-1-Thread-5. Das Ergebnis sollte 5 sein. Der aktuelle Thread ist Pool-1-Thread-7 Das Ergebnis sollte 7 sein. Der aktuelle Thread ist Pool-1-Thread-9. Das Ergebnis sollte 9 sein. Der aktuelle Thread ist Pool-1-Thread-8. Das Ergebnis sollte 8 sein. Die Aufgabe wurde für Pool erledigt. 1-Thread-9 Das Ergebnis sollte 9 sein. Das Ergebnis ist 9 Die Aufgabe wurde für Pool-1-Thread-8 erledigt. Das Ergebnis sollte 8 sein. Die Aufgabe wurde für Pool-1-Thread-7 erledigt. Das Ergebnis sollte 7 sein. Die Aufgabe wurde erledigt Pool-1-Thread-6 Das Ergebnis sollte 6 die Aufgabe sein, für die erledigt wurdePool-1-Thread-5 Das Ergebnis sollte 5 sein. Die Aufgabe wurde für Pool-1-Thread-4 erledigt. Das Ergebnis sollte 4 sein. Die Aufgabe wurde für Pool-1-Thread-3 erledigt. Das Ergebnis sollte 3 sein
Die für Pool-1-Thread-2 abgeschlossene Aufgabe sollte 2 sein
Der aktuelle Thread ist Pool-1-Thread-1. Das Ergebnis sollte 1 sein. Der aktuelle Thread ist Pool-1-Thread-3. Das Ergebnis sollte 3 sein. Der aktuelle Thread ist Pool-1-Thread-2. Das Ergebnis sollte 2 der aktuelle sein Thread ist Pool-1-Thread-5 Das Ergebnis sollte 5 sein. Der aktuelle Thread ist Pool-1-Thread-4. Das Ergebnis sollte 4 sein. Der aktuelle Thread ist Pool-1-Thread-6. Das Ergebnis sollte 6 sein. Der aktuelle Thread ist Pool-1-Thread-7 Das Ergebnis sollte 7 sein. Der aktuelle Thread ist Pool-1-Thread-8. Das Ergebnis sollte 8 sein. Der aktuelle Thread ist Pool-1-Thread-9. Das Ergebnis sollte 9 sein. Die Aufgabe wurde für Pool erledigt. 1-Thread-9 Das Ergebnis sollte 9 sein. Die Aufgabe wurde für Pool-1-Thread-8 erledigt. Das Ergebnis sollte 8 sein. Die Aufgabe wurde für Pool-1-Thread-7 erledigt. Das Ergebnis sollte 7 sein. Die Aufgabe wurde für Pool-1 erledigt. Thread-6 das Ergebnis sollte 6 die Aufgabe sein, die für Pool-1-Thread-5 das Ergebnis erledigt wurdesollte 5 die Aufgabe sein, die für Pool-1-Thread-4 erledigt wurde, das Ergebnis sollte 4 die Aufgabe sein, die für Pool-1-Thread-3 erledigt wurde, das Ergebnis sollte 3 sein, die Aufgabe sollte für Pool-1-Thread-2 erledigt sein, das Ergebnis sollte sein 2 Die Aufgabe für Pool-1-Thread-1 abgeschlossen. Das Ergebnis sollte 1 sein. Das Ergebnis der Zukunft ist 1
================================================== =====
Für Executorservice ist das Ergebnis erst verfügbar, nachdem alle Aufgaben erledigt wurden.
Executor Completeservice Jedes verfügbare Ergebnis macht diese Rückgabe.
quelle