我正在寻找更好的方法来“关闭”某些资源,这里摧毁Process
链中的外部CompletableFuture
。现在我的代码大致如下:
public CompletableFuture<ExecutionContext> createFuture()
{
final Process[] processHolder = new Process[1];
return CompletableFuture.supplyAsync(
() -> {
try {
processHolder[0] = new ProcessBuilder(COMMAND)
.redirectErrorStream(true)
.start();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return PARSER.parse(processHolder[0].getInputStream());
}, SCHEDULER)
.applyToEither(createTimeoutFuture(DURATION), Function.identity())
.exceptionally(throwable -> {
processHolder[0].destroyForcibly();
if (throwable instanceof TimeoutException) {
throw new DatasourceTimeoutException(throwable);
}
Throwables.propagateIfInstanceOf(throwable, DatasourceException.class);
throw new DatasourceException(throwable);
});
}
我看到的问题是一个“hacky”单元素数组,它保存对进程的引用,以便在出错时可以关闭它。是否有一些CompletableFuture
API允许将一些“上下文”传递给exceptionally
(或其他一些方法来实现)?
我正在考虑自定义CompletionStage
实现,但删除“holder”变量似乎是一项重大任务。
答案 0 :(得分:2)
不需要CompletableFuture
s的线性链。实际上,你已经没有因为createTimeoutFuture(DURATION)
因为实现超时而非常复杂。你可以这样说:
public CompletableFuture<ExecutionContext> createFuture() {
CompletableFuture<Process> proc=CompletableFuture.supplyAsync(
() -> {
try {
return new ProcessBuilder(COMMAND).redirectErrorStream(true).start();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}, SCHEDULER);
CompletableFuture<ExecutionContext> result
=proc.thenApplyAsync(process -> PARSER.parse(process.getInputStream()), SCHEDULER);
proc.thenAcceptAsync(process -> {
if(!process.waitFor(DURATION, TimeUnit.WHATEVER_DURATION_REFERS_TO)) {
process.destroyForcibly();
result.completeExceptionally(
new DatasourceTimeoutException(new TimeoutException()));
}
});
return result;
}
如果你想保持未来的未来,也许你认为流程启动时间很重要,你可以使用
public CompletableFuture<ExecutionContext> createFuture() {
CompletableFuture<Throwable> timeout=createTimeoutFuture(DURATION);
CompletableFuture<Process> proc=CompletableFuture.supplyAsync(
() -> {
try {
return new ProcessBuilder(COMMAND).redirectErrorStream(true).start();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}, SCHEDULER);
CompletableFuture<ExecutionContext> result
=proc.thenApplyAsync(process -> PARSER.parse(process.getInputStream()), SCHEDULER);
timeout.exceptionally(t -> new DatasourceTimeoutException(t))
.thenAcceptBoth(proc, (x, process) -> {
if(process.isAlive()) {
process.destroyForcibly();
result.completeExceptionally(x);
}
});
return result;
}
答案 1 :(得分:1)
我自己使用了一个项目数组来模拟Java中适当的闭包。
另一个选择是使用带字段的私有静态类。优点是它使目的更清晰,对具有大封闭的垃圾收集器的影响稍小,即具有N个字段的对象与长度为1的N个数组。如果您需要关闭相同的字段,它也会变得有用在其他方法。
这是事实上的模式,甚至超出CompletableFuture
的范围,并且在lambdas是Java中的东西之前很久就已经(ab)使用了它,例如匿名课程。所以,不要感到如此糟糕,只是Java的进化并没有为我们提供适当的封闭(但是?永远?)。
如果需要,可以从CompletableFuture
内的.handle()
返回值,这样就可以完整地包装完成结果并返回一个包装器。在我看来,这并不比手动闭包更好,并补充说你将来会创造这样的包装。
不需要对CompletableFuture
进行子类化。你对改变它的行为不感兴趣,只是在附加数据时,你可以用当前Java的最终变量捕获来做。也就是说,除非你剖析并看到创建这些闭包实际上在某种程度上影响了性能,我非常怀疑。