当CompleteableFuture既没有完成,取消也没有例外时会发生什么?

时间:2017-10-05 21:41:10

标签: java asynchronous java-8 garbage-collection completable-future

... vs使用回调?

第一个例子,带回调

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

}

问题是:

  • 示例2中的cf会发生什么/(何时)垃圾收集会将其删除?
  • 有没有更好的方式来翻译&#34;示例1进入示例2?

在你的回答中,请保持对技术论点的敏锐而不是偏好/可改进的代码(因为这不是这个问题的内容),也不是因为cf.cancel没有被调用。

2 个答案:

答案 0 :(得分:1)

CompletableFuture会怎样?何时通过垃圾收集将其删除?

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

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

另请注意,传递给thenAcceptAsync()的lambda需要保持至少与可完成的未来未完成一样长,除非可完成的未来本身是gc'd。因此,即使你没有保留对它的引用,这个lambda也可能比执行方法的寿命更长。

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

对于局部变量,很容易看到它们何时停止引用它。对于捕获的参考,遗憾的是,只要lambda存在(即使它不再使用它),它将保持不变。假设优化,当计划不再执行时,lambda将符合GC的条件 - 所以这就是关闭执行程序的时候。

一旦这些参考文献消失,它就有资格获得GC。

有没有更好的方法将示例1“翻译”为示例2?

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

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

如果这是一次性执行,那么转换是正确的,但是仅仅依靠CompletableFuture.supplyAsync()而不是自己处理未来会更好。这也会自动提供异常处理功能。

答案 1 :(得分:-3)

在示例1中,当您致电a时,会调用b来安排任务,并a返回。在某些时候,回调将由运行b内创建的任务的线程执行。

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

如果你要在代码中的某个地方调用get,它将阻塞直到任务完成,然后在调用{thenAcceptAsync的线程的上下文中执行该子句1}}。

没有办法get示例1到示例2,因为他们正在做两件完全不同的事情,根据您的要求,每件事都可能是正确的。

另外一件事你似乎很困惑:期货既没有完成也没有取消,他们指向的任务是ARE。它们只能提供将来指向的任务结果。