2017-03-09 4 views
5

Ich habe eine Methode für eine Repository-Klasse, die eine CompletableFuture zurückgibt. Der Code, der diese Futures vervollständigt, verwendet eine Bibliothek von Dritten, die blockiert. Ich beabsichtige, eine separate begrenzte Executor zu haben, die diese Repository-Klasse verwenden wird, um diese blockierenden Aufrufe zu machen. HierWelcher Executor wird beim Erstellen von Java CompleableFutures verwendet?

ein Beispiel:

public class PersonRepository { 
    private Executor executor = ... 
    public CompletableFuture<Void> create(Person person) {...} 
    public CompletableFuture<Boolean> delete(Person person) {...} 
} 

Der Rest meiner Anwendung werden diese Futures und tun einige andere Dinge mit den Ergebnissen zusammenstellen. Wenn diese anderen Funktionen, die an thenAccept, thenCompose, whenComplete usw. geliefert werden, möchte ich nicht, dass sie auf dem Executor für das Repository ausgeführt werden.

Ein weiteres Beispiel:

public CompletableFuture<?> replacePerson(Person person) { 
    final PersonRepository repo = getPersonRepository(); 
    return repo.delete(person) 
     .thenAccept(existed -> { 
      if (existed) System.out.println("Person deleted")); 
      else System.out.println("Person did not exist")); 
     }) 
     .thenCompose(unused -> { 
      System.out.println("Creating person"); 
      return repo.create(person); 
     }) 
     .whenComplete((unused, ex) -> { 
      if (ex != null) System.out.println("Creating person"); 
      repo.close(); 
     }); 
} 

javadoc Zustände:

Aktionen für abhängige Beendigungen von nicht-Async Verfahren geliefert wird, kann durch den Thread durchgeführt werden, die den aktuellen CompletableFuture abgeschlossen ist, oder durch jede andere Aufrufer einer Abschlussmethode.

Side Frage: Warum gibt es eine oder hier? In welchem ​​Fall gibt es einen anderen Aufrufer einer Abschlussmethode, die die aktuelle Zukunft nicht abschließt?

Hauptfrage: Wenn ich will alle println von einem anderen Executor als die vom Repository verwendet ein ausgeführt werden, welche Methoden muss ich async machen und die Vollstrecker von Hand zur Verfügung stellen?

Offensichtlich muss die thenAccept in thenAcceptAsync geändert werden, aber ich bin nicht sicher über diesen Punkt weiter.

Alternative Frage: Welcher Thread vervollständigt die zurückgegebene Zukunft von ?

Meine Vermutung ist, dass es sein wird, was auch immer Thread die Zukunft aus dem Funktionsargument ergänzt. Mit anderen Worten, ich müsste auch whenComplete zu whenCompleteAsync ändern.

Vielleicht bin ich über komplizierende Dinge, aber das fühlt sich an, als könnte es ziemlich schwierig werden. Ich muss sehr darauf achten, woher all diese Futures kommen. Wenn ich eine Zukunft zurückgebe, wie kann ich verhindern, dass Anrufer meinen Executor verwenden? Es fühlt sich an, als ob es die Kapselung bricht. Ich weiß, dass alle Transformationsfunktionen in Scala eine implizite ExecutionContext annehmen, die all diese Probleme zu lösen scheint.

+2

Lesen Sie den Artikel - http://www.nurkiewicz.com/2015/11/which-thread-executes.html. Es hilft, ein Licht auf Threads Arbeit Schatten zu werfen – rvit34

Antwort

4

Side-Frage: Wenn Sie das Intermediate CompletionStage einer Variablen zugewiesen und eine Methode darauf aufgerufen haben, würde es auf dem gleichen Thread ausgeführt werden.

Hauptfrage: Nur die erste, also ändern thenAccept zu thenAcceptAsync - alle folgenden werden ihre Schritte auf den Thread ausführen, der für die Annahme verwendet wird.

Alternative Frage: Der Thread, der die Zukunft von thenCompose abgeschlossen hat, ist derselbe, der für den Compose verwendet wurde.

Sie sollten sich die CompletionStages als Schritte vorstellen, die in schneller Abfolge auf demselben Thread ausgeführt werden (indem Sie einfach die Funktionen der Reihe nach anwenden), es sei denn, Sie möchten den Schritt in einem anderen Thread asynchron ausführen. Alle nächsten Schritte werden dann für diesen neuen Thread ausgeführt.

In Ihrem aktuellen Setup würde die Schritte wie folgt ausgeführt werden:

Thread-1: delete, accept, compose, complete 

Mit dem ersten akzeptieren async, wird es:

Thread-1: delete 
Thread-2: accept, compose, complete 

Was Ihre letzte Frage, etwa die gleichen Thread Wesen von Ihren Anrufern verwendet, wenn sie zusätzliche Schritte hinzufügen - ich glaube nicht, dass es viel gibt, was Sie tun können, abgesehen davon, dass Sie keine CompletableFuture zurückgeben, sondern eine normale Future.

+0

Ihre Antwort auf die Nebenfrage macht keinen Sinn für mich. Die Zuordnung sollte keinen Unterschied machen. Deine Antworten widersprechen auch dem, was @ Joe-c gesagt hat, und dem Artikel im obigen Kommentar. Wenn ich verstanden habe, was sie gesagt haben, wird nichts notwendigerweise auf demselben Thread (ohne Async) ausgeführt, es sei denn, sie sind während der Komposition entweder vollständig oder unvollständig. Wenn zu irgendeinem Zeitpunkt während der Komposition die Zwischenzukunft abgeschlossen ist, wird die nächste Komposition im aktuellen Thread ausgeführt. – Steiny

+0

Ja, ich habe das unklar formuliert, es ist eher so, dass wenn man eine Bühne lange genug herumhielt und es bereits fertig ist, dann würde es mehr Kompositionen auf dem Anrufer-Thread geben. So dass "oder" in den Dokumenten grundsätzlich für den Fall ist, dass die Etappen bereits abgeschlossen sind. – john16384

+0

Vielleicht ein wenig verwirrend angesichts der anderen Antworten, aber das ist die richtige Antwort für die Hauptfrage. Sobald ein Executor explizit an einen asynchronen Kombinator übergeben wurde, werden sowohl das Funktionsargument als auch die resultierende Zukunft von diesem Executor ausgeführt. – Steiny

4

Nur aus meinen empirischen Beobachtungen beim Herumspielen hängt der Thread, der diese nicht-asynchronen Methoden ausführt, davon ab, was zuerst passiert, thenCompose selbst oder die Aufgabe hinter der Future.

Wenn thenCompose zuerst ausgeführt wird (was in Ihrem Fall fast sicher ist), wird die Methode auf demselben Thread ausgeführt, der die Future Aufgabe ausführt.

Wenn die Aufgabe hinter Ihrem Future zuerst abgeschlossen wird, wird die Methode sofort auf den aufrufenden Thread (d. H. Kein Executor) ausgeführt.

+0

Ah ja natürlich. Ich hatte nicht daran gedacht, dass die andere Zukunft tatsächlich abgeschlossen sein könnte, bevor der aktuelle Thread sie fertig komponiert hat (obwohl, wie du angemerkt hast, dies in meinem Fall ziemlich unwahrscheinlich ist). Ich wundere mich immer noch über die innere Zukunft, indem ich die gleiche Logik anwende, dass es so aussieht, als ob der daraus resultierende vom Exekutor immer noch vervollständigt werden könnte. Ich muss vielleicht etwas mehr experimentieren – Steiny

Verwandte Themen