UUID.randomUUID() 的 CompletableFuture 问题

CompletableFuture issue with UUID.randomUUID()

所以我决定开始使用 Java8 中的 CompletableFuture,但我无法弄清楚这段代码有什么问题:

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";
    }

其中 User class 定义为:

@Data
@AllArgsConstructor
public class User {
    private Long id;
    private String username;
    private String password;
    private String role;
}

我得到的行为是:

有人可以尝试向我解释为什么我在使用 UUID 时会出现这种奇怪的行为吗?

PS: 刚开始学习CompletableFuture想着parallel Futures,无意间就想到了这个

此致。

这与 UUID 无关,只是它的生成需要一些时间并且您不需要等待完成。

由于所有操作都发生在后台线程中,并且您从 main 方法返回,JVM 将确定不再有非守护线程 运行 并终止。

只需添加一个等待完成的操作:

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

请注意,在您的原始代码中,您使用的是 .allOf(futureUser, futureUser) 而不是 .allOf(futureUser, futureUUID),因此链接操作可能会在 futureUUID 尚未完成时执行,这可能会导致工作线程在 futureUUID.join() 调用中被阻塞。

如果您使用

,您的代码会简单得多
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

这也不受 allOf 所犯错误的影响,因为在工作线程中不需要 join() 调用。