5

Ich habe den folgenden Code (resultierend aus my previous question), der eine Aufgabe auf einem Remote-Server plant und dann zur Fertigstellung unter Verwendung von ScheduledExecutorService#scheduleAtFixedRate abfragt. Sobald die Aufgabe abgeschlossen ist, wird das Ergebnis heruntergeladen. Ich möchte eine Future an den Aufrufer zurückgeben, so dass sie entscheiden können, wann und wie lange zu blockieren, und geben Sie ihnen die Option, die Aufgabe abzubrechen.ComplectableFuture # whenComplete wird nicht aufgerufen, wenn thenApply verwendet wird

Mein Problem ist, dass, wenn der Client die Future von der download zurückgegebene Methode storniert, whenComplete Block nicht ausgeführt wird. Wenn ich thenApply entferne, tut es das. Es ist offensichtlich, dass ich etwas falsch verstehe über Future Zusammensetzung ... Was soll ich ändern?

public Future<Object> download(Something something) { 
    String jobId = schedule(something); 
    CompletableFuture<String> job = pollForCompletion(jobId); 
    return job.thenApply(this::downloadResult); 
} 

private CompletableFuture<String> pollForCompletion(String jobId) { 
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 
    CompletableFuture<String> completionFuture = new CompletableFuture<>(); 

    ScheduledFuture<?> checkFuture = executor.scheduleAtFixedRate(() -> {   
      if (pollRemoteServer(jobId).equals("COMPLETE")) { 
       completionFuture.complete(jobId); 
      } 
    }, 0, 10, TimeUnit.SECONDS); 
    completionFuture     
      .whenComplete((result, thrown) -> { 
       System.out.println("XXXXXXXXXXX"); //Never happens unless thenApply is removed 
       checkFuture.cancel(true); 
       executor.shutdown(); 
      }); 
    return completionFuture; 
} 

Auf die gleiche Note, wenn ich es tue:

return completionFuture.whenComplete(...) 

statt

completionFuture.whenComplete(...); 
return completionFuture; 

whenComplete führt auch nie. Dies scheint mir sehr widersprüchlich zu sein. Sollte nicht logischerweise die Future zurückgegeben von whenComplete die sein, an der ich mich festhalten sollte?

EDIT:

ich meinen Code geändert, um explizit die Stornorück propagieren. Es ist abscheulich und nicht lesbar, aber es funktioniert und ich konnte nicht einen besseren Weg finden:

public Future<Object> download(Something something) throws ChartDataGenException, Exception { 
     String jobId = schedule(report); 
     CompletableFuture<String> job = pollForCompletion(jobId); 
     CompletableFuture<Object> resulting = job.thenApply(this::download); 
     resulting.whenComplete((result, thrown) -> { 
      if (resulting.isCancelled()) { //the check is not necessary, but communicates the intent better 
       job.cancel(true); 
      } 
     }); 
     return resulting; 
} 
+0

Es gibt nicht einmal den 'whenComplete' Block. Ich setze einen Haltepunkt und ein 'System.out.print' hinein, und weder der Haltepunkt wird getroffen, noch wird die Zeile gedruckt. Beides passiert, wenn ich das 'thenApply'-Bit lösche. – kaqqao

+0

Nach meinem Verständnis sollte es umgekehrt sein, was Sie melden. 'completionFuture.whenComplete()' ist eine reine Funktion und sollte nichts an der Arbeitsweise von 'completionFuture' ändern. Wenn Sie das Ergebnis von 'whenComplete' nicht zurückgeben, sollte es nicht mehr erreichbar sein und GC unterliegen. –

+0

Absolut einverstanden. Aber das ist, was ich sehe ... Wenn ich das Ergebnis von 'whenComplete' zurückgebe und es sofort abbringe, bekomme ich XXXXX nicht in der Konsole. Auf der anderen Seite, wenn ich die ursprüngliche 'completionFuture' und' cancel' _that_ zurückschicke, tue ich es. – kaqqao

Antwort

4

Ihre Struktur ist wie folgt:

  ┌──────────────────┐ 
      │ completionFuture | 
      └──────────────────┘ 
      ↓    ↓ 
    ┌──────────────┐  ┌───────────┐ 
    │ whenComplete |  │ thenApply | 
    └──────────────┘  └───────────┘ 

Also, wenn Sie kündigen die thenApply Zukunft, die ursprüngliche completionFuture Objekt bleibt unberührt, da es nicht von der Stufe thenApply abhängt. Wenn Sie jedoch die Stufe thenApply nicht verketten, geben Sie die ursprüngliche completionFuture Instanz zurück, und das Abbrechen dieser Stufe führt zur Stornierung aller abhängigen Stufen, wodurch die Aktion whenComplete sofort ausgeführt wird.

Aber wenn die Stufe thenApply abgebrochen wird, kann die completionFuture noch abgeschlossen werden, wenn die pollRemoteServer(jobId).equals("COMPLETE") Bedingung erfüllt ist, da diese Abfrage nicht beendet wird. Aber wir kennen die Beziehung jobId = schedule(something) und pollRemoteServer(jobId) nicht. Wenn Ihre Anwendung Zustandsänderungen in einer Art und Weise, dass diese Bedingung kann nie einen Download nach dem Abbrechen, diese Zukunft wird nie abgeschlossen ...


In Bezug auf Ihre letzte Frage erfüllt werden, die Zukunft ist „diejenige, die ich festhalten soll? "Es gibt keine Notwendigkeit, eine lineare Kette von Futures zu haben, während die Bequemlichkeitsmethoden von CompletableFuture es einfach machen, eine solche Kette zu erstellen, mehr als oft, es ist die am wenigsten nützliche Sache zu tun, wie Sie einfach ein schreiben könnten Codeblock, wenn Sie eine lineare Abhängigkeit haben. Ihr Modell der Verkettung von zwei unabhängigen Stufen ist richtig, aber die Aufhebung funktioniert nicht durch sie, aber es würde auch nicht durch eine lineare Kette funktionieren.

Wenn Sie die Quellphase abbrechen möchten, benötigen Sie einen Verweis darauf. Wenn Sie jedoch das Ergebnis einer abhängigen Stufe erhalten möchten, benötigen Sie auch eine Referenz auf diese Stufe.

+0

OP benötigt jedoch nicht das Ergebnis von 'whenComplete'. Ich bin jedoch der Meinung, dass diese Phase explizit beibehalten werden muss, da sie sonst nicht erreichbar ist und zum Zeitpunkt der Stornierung möglicherweise nicht existiert. –

+0

@MarkoTopolnik Ich nehme an, die ursprüngliche Zukunft, die Sie 'whenComplete' nennen, behält den Verweis auf den verketteten ... Sonst wäre es ernsthaft verrückt. – kaqqao

+0

@kaqqao Es ist wahrscheinlich richtig wegen der Art, wie man dies erwartet, aber es ist immer noch unspezifiziertes Verhalten und ungesund auf das man sich verlassen kann. –

Verwandte Themen