... 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;
}
}
问题是:
在你的回答中,请保持对技术论点的敏锐而不是偏好/可改进的代码(因为这不是这个问题的内容),也不是因为cf.cancel没有被调用。
答案 0 :(得分:1)
CompletableFuture
会怎样?何时通过垃圾收集将其删除?在您的代码中,您有3个CompletableFuture
的引用:
cf
a()
result
b()
SCHEDULED_EXECUTOR.schedule()
另请注意,传递给thenAcceptAsync()
的lambda需要保持至少与可完成的未来未完成一样长,除非可完成的未来本身是gc'd。因此,即使你没有保留对它的引用,这个lambda也可能比执行方法的寿命更长。
没有什么特别适用于CompletableFuture
,标准GC规则适用:当没有任何引用它时它将有资格进行垃圾收集 - 或者更准确地说,当GC根不再存在路径时。
对于局部变量,很容易看到它们何时停止引用它。对于捕获的参考,遗憾的是,只要lambda存在(即使它不再使用它),它将保持不变。假设优化,当计划不再执行时,lambda将符合GC的条件 - 所以这就是关闭执行程序的时候。
一旦这些参考文献消失,它就有资格获得GC。
翻译实际上并不正确,因为CompletableFuture
只能完成一次。在示例1中,将为每次成功执行调用回调,但在示例2中,只有第一个成功的回调将触发它。实际上会忽略对result.complete(s)
的下一次调用。
CompletableFuture
不是为处理结果值流而设计的。为此,您应该使用可观察和订阅者进行反应式编程。
如果这是一次性执行,那么转换是正确的,但是仅仅依靠CompletableFuture.supplyAsync()
而不是自己处理未来会更好。这也会自动提供异常处理功能。
答案 1 :(得分:-3)
在示例1中,当您致电a
时,会调用b
来安排任务,并a
返回。在某些时候,回调将由运行b
内创建的任务的线程执行。
在示例2中,当您致电a
时,会调用b
来安排任务并a
返回。永远不会执行thenAcceptAsync
内的代码,因为您从未调用过Future get
。 cf
是一个局部变量,因此a
终止后,它指向的对象可用于GC。在某些时候,GC可能会删除它。
如果你要在代码中的某个地方调用get
,它将阻塞直到任务完成,然后在调用{thenAcceptAsync
的线程的上下文中执行该子句1}}。
没有办法get
示例1到示例2,因为他们正在做两件完全不同的事情,根据您的要求,每件事都可能是正确的。
另外一件事你似乎很困惑:期货既没有完成也没有取消,他们指向的任务是ARE。它们只能提供将来指向的任务结果。