So I decided to start using the CompletableFuture
in Java8 and I cannot figure out what wrong with this snippet:
public static void main(String...strings) throws Exception {
final Supplier<User> makeUserSupplier = () -> makeUser();
final Supplier<String> uuidSupplier = () -> makeUUID();
final CompletableFuture<User> futureUser = CompletableFuture.supplyAsync(makeUserSupplier);
final CompletableFuture<String> futureUUID = CompletableFuture.supplyAsync(uuidSupplier);
CompletableFuture.allOf(futureUser, futureUUID)
.thenApplyAsync(aVoid -> {
final User user = futureUser.join();
final String uuid = futureUUID.join();
return "received user + " + user + " and uuid is " + uuid ;
})
.handle((ok, e) -> {
System.out.println("ok----" + ok);
System.out.println("e----" + e);
return null;
});
}
private static User makeUser() throws RuntimeException {
// throw new RuntimeException("lkj");
return new User(1L, "mm", "ll", "kk");
}
private static String makeUUID() throws RuntimeException {
return UUID.randomUUID().toString();
// return "dummy";
}
where the User
class is defined as:
@Data
@AllArgsConstructor
public class User {
private Long id;
private String username;
private String password;
private String role;
}
The behavior I get is:
UUID.randomUUID().toString()
and I get result when I use some random String
.final String uuid = futureUUID.join();
and then my program stops with no result.Can someone please try to explain to me why I am getting this strange behavior when using UUID ?
PS: I just started learning CompletableFuture
and thought about parallel Futures, then accidentally came to this.
Best regards.
This has nothing to do with the UUID, except that its generation takes some time and you’re not waiting for the completion.
Since all operations happen in background threads and you’re returning from the main
method, the JVM will determine that no non-Daemon thread is running anymore and terminate.
Simply add a wait-for-completion operation:
final Supplier<User> makeUserSupplier = () -> makeUser();
final Supplier<String> uuidSupplier = () -> makeUUID();
final CompletableFuture<User> futureUser = CompletableFuture.supplyAsync(makeUserSupplier);
final CompletableFuture<String> futureUUID = CompletableFuture.supplyAsync(uuidSupplier);
CompletableFuture.allOf(futureUser, futureUUID)
.thenApplyAsync(aVoid -> {
final User user = futureUser.join();
final String uuid = futureUUID.join();
return "received user + " + user + " and uuid is " + uuid ;
})
.handle((ok, e) -> {
System.out.println("ok----" + ok);
System.out.println("e----" + e);
return null;
})
.join(); // wait for completion
Note that in your original code, you were using .allOf(futureUser, futureUser)
instead of .allOf(futureUser, futureUUID)
, so the chained operation might get executed when futureUUID
has not completed yet, which could cause a worker thread getting blocked in the futureUUID.join()
call.
Your code would be much simpler if you used
final CompletableFuture<User> futureUser = CompletableFuture.supplyAsync(() -> makeUser());
final CompletableFuture<String> futureUUID = CompletableFuture.supplyAsync(() -> makeUUID());
futureUser.thenCombineAsync(futureUUID, (user, uuid) -> {
return "received user + " + user + " and uuid is " + uuid;
})
.handle((ok, e) -> {
System.out.println("ok----" + ok);
System.out.println("e----" + e);
return null;
})
.join(); // wait for completion
which is also immune to the mistake made with allOf
, as no join()
call is needed within a worker thread.