当 CompleteableFuture 既没有完成、取消也没有异常时会发生什么?

What happens to CompleteableFuture when it gets neither completed, cancelled nor an exception?

...与使用回调相比?

第一个例子,带回调

public class NewClass {

    public static final ScheduledExecutorService SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(1);

    @FunctionalInterface
    public interface F_CallbackDef {

        void callback(String s);
    }

    public void a() {
        b((String s) -> {
            System.out.println(s);
        });
    }

    public void b(F_CallbackDef callback) {
        SCHEDULED_EXECUTOR.schedule(() -> {
            String s = "string";
            Random random = new Random();
            boolean cond = random.nextBoolean();
            if (cond) {
                boolean cond2 = random.nextBoolean();
                if (cond2) {
                   // assume possible uncatched exeption
                }
                callback.callback(s);
                // in every case:
                // callback just moves out of scope - no problem
            }
        }, 1, TimeUnit.HOURS);
    }
}

第二个例子,CompletableFuture

public class NewClass1 {

    public static final ScheduledExecutorService SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(1);

    public void a() {
        CompletableFuture<String> cf = b();
        cf.thenAcceptAsync((String s) -> {
            System.out.println(s);
        });

        // cf moves out of scope immediatly
        // but also it gets never completed, nor cancelled
    }

    CompletableFuture<String> b() {
        CompletableFuture<String> result = new CompletableFuture<>();
        SCHEDULED_EXECUTOR.schedule(() -> {
            String s = "string";
            Random random = new Random();
            boolean cond = random.nextBoolean();
            if (cond) {
                boolean cond2 = random.nextBoolean();
                if (cond2) {
                    // assume possible uncatched exception
                }
                result.complete(s);
            }

            // assume cond = false or cond2 = false
            // this is not about that result.cancel() SHOULD be called, it is a bug if you will
        }, 1, TimeUnit.HOURS);
        return result;
    }

}

问题是:

在您的回答中,请对技术论点保持敏锐,而不是 preferred/improvable 代码(因为这不是这个问题的目的),也不是 cf.cancel 没有得到打电话。

在示例 1 中,当您调用 a 时,它会调用 b 来安排任务,并调用 a returns。在某些时候,回调将由 运行 在 b.

中创建的任务线程执行

在示例 2 中,当您调用 a 时,它会调用安排任务的 ba returns。 thenAcceptAsync 中的代码永远不会执行,因为您从未调用过 Future 的 getcf 是局部变量,因此它指向的对象在 a 终止后可用于 GC。在某些时候 GC 可能会删除它。

如果您要在代码中的某处调用 get,它将阻塞直到任务完成,然后在调用线程的上下文中执行 thenAcceptAsync 中的子句get.

无法从示例 1 到示例 2 translate,因为它们在做 2 件完全不同的事情,根据您的要求,每件事情都可能是正确的。

您似乎对另一件事感到困惑:期货既未完成也未取消,它们指向的任务是 ARE。他们只提供在未来获得他们指向的任务结果的能力。

CompletableFuture 会怎样?什么时候会被垃圾收集删除?

在您的代码中,您有 3 个对 CompletableFuture:

的引用
  • a()
  • 中的局部变量cf
  • b()
  • 中的局部变量result
  • lambda 中捕获的引用传递给 SCHEDULED_EXECUTOR.schedule()

另请注意,至少只要可完成的未来未完成,传递给 thenAcceptAsync() 的 lambda 就需要保留,除非可完成的未来本身是 gc'd。因此,即使您不保留对它的引用,此 lambda 也可能比方法的执行时间更长。

关于 CompletableFutures 没有什么特别适用的,标准 GC 规则适用:当没有人再引用它时,它将有资格进行垃圾收集 - 或者更准确地说,当不再存在到 GC 根的路径时。

对于局部变量,很容易看出它们何时停止引用它。对于捕获的引用,不幸的是,只要 lambda 存在,它就会一直存在(即使它不再使用它)。假设优化,当没有更多的执行计划时,lambda 将符合 GC 的条件——所以这里就是当您关闭执行程序时。

一旦这些引用消失,它将有资格进行 GC。

是否有更好的方法将示例 1 "translate" 转换为示例 2?

翻译实际上并不正确,因为CompletableFuture只能完成一次。在示例 1 中,每次成功执行都会调用您的回调,但在示例 2 中,只有第一个成功的执行才会触发它。对 result.complete(s) 的下一次调用实际上将被忽略。

CompletableFuture 不是为处理结果值流而设计的。为此,您应该使用可观察对象和订阅者进行反应式编程。

如果这是针对单次执行的,则转换是正确的,但是最好只依赖 CompletableFuture.supplyAsync() 而不是自己处理未来。这也将自动提供异常处理功能。