2016-06-20 24 views
5

Quindi ho un metodo che restituisce un CompletableFuture. Prima di tornare, questo metodo aggiunge un blocco con thenAccept che viene eseguito dopo il completamento dello CompletableFuture.Qual è l'ordine in cui vengono eseguiti più blocchi ThenAccept di un CompletableFuture

Il chiamante di questo metodo aggiunge anche un altro blocco con thenAccept. Ovviamente questo può andare avanti con più chiamate concatenate.

In che ordine viene eseguito il CompletionStage restituito dalle chiamate thenAccept? È garantito essere l'ordine in cui vengono aggiunti? In caso negativo, come si può garantire che vengano eseguiti nell'ordine in cui sono stati aggiunti?

PS: chiedo questo in base alla mia esperienza con CompletableFuture e questo article

+2

"thenAccept" significa che il primo "questo" CompletableFuture è stato completato e solo successivamente viene eseguita la funzione del parametro. –

risposta

6

si sta modellando le dipendenze di stadi di completamento da loro concatenamento. Se si concatenano due azioni A e B a un'altra azione C, si definiscono i rapporti A → C e B → C, ma nessuna relazione tra A e B e, pertanto, non v'è alcuna relazione, tra cui nessun rapporto ordinamento, tra di loro, in altre parole, è possibile 't anche per scontato che uno verrà eseguito dopo l'altro, vale a dire

CompletableFuture<String> base=CompletableFuture.supplyAsync(() -> { 
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); 
    return "source"; 
}); 
base.thenAccept(s -> { 
    System.out.println("entered first consumer in "+Thread.currentThread()); 
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); 
    System.out.println("leaving first consumer"); 
}); 
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); 
base.thenAccept(s -> { 
    System.out.println("entered second consumer in "+Thread.currentThread()); 
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); 
    System.out.println("leaving second consumer"); 
}); 

sarà molto probabile che qualcosa di stampa come

entered first consumer in Thread[ForkJoinPool.commonPool-worker-1,5,main] 
entered second consumer in Thread[main,5,main] 
leaving second consumer 
leaving first consumer 

anche se, naturalmente, non v'è alcuna garanzia su di esso.


Per imporre la vostra dipendenza tra i due consumatori, si deve a catena in modo appropriato, per esempio

CompletableFuture<String> base=CompletableFuture.supplyAsync(() -> { 
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); 
    return "source"; 
}); 
CompletableFuture<Void> next = base.thenAccept(s -> { 
    System.out.println("entered first consumer in "+Thread.currentThread()); 
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); 
    System.out.println("leaving first consumer"); 
}); 
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); 
base.thenAcceptBoth(next, (s,ignored) -> { 
    System.out.println("entered second consumer in "+Thread.currentThread()); 
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); 
    System.out.println("leaving second consumer"); 
}).join(); 

Qui, il secondo consumatore è incatenato al base e next, per ricevere il risultato da base, ma dipendono dal completamento next s'(che normalmente non richiederebbe se non c'è risultato di passare, forse vuoi ripensare il tuo design, se hai uno scenario del genere).

In alternativa, è possibile convertire il primo Consumer ad un Function che passa attraverso il valore, in modo da poter concatenare via thenApply, per consentire il concatenamento un'altra thenAccept fase ad esso.