Java 在可完成的未来链中关闭外部流程

Java 在可完成的未来链中关闭外部流程,java,java-8,completable-future,Java,Java 8,Completable Future,我正在寻找更好的方法来“关闭”一些资源,在CompletableFuture链中销毁外部Process。现在,我的代码大致如下所示: public CompletableFuture<ExecutionContext> createFuture() { final Process[] processHolder = new Process[1]; return CompletableFuture.supplyAsync( () -> {

我正在寻找更好的方法来“关闭”一些资源,在
CompletableFuture
链中销毁外部
Process
。现在,我的代码大致如下所示:

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);
            });
}
public CompletableFuture createFuture()
{
最终流程[]流程持有者=新流程[1];
返回CompletableFuture.SupplySync(
() -> {
试一试{
processHolder[0]=新的ProcessBuilder(命令)
.redirectErrorStream(真)
.start();
}捕获(IOE异常){
抛出新的未选中异常(e);
}
返回PARSER.parse(processHolder[0].getInputStream());
},调度程序)
.applytoother(createTimeoutFuture(DURATION),Function.identity())
.例外情况下(可丢弃->{
processHolder[0]。强制销毁();
if(TimeoutException的可丢弃实例){
抛出新的DatasourceTimeoutException(可丢弃);
}
PropagativeInstanceof(throwable,DatasourceException.class);
抛出新的DatasourceException(throwable);
});
}
我看到的问题是一个“黑”的单元素数组,它保存对进程的引用,以便在出错时关闭它。是否有一些
CompletableFuture
API允许异常地将一些“上下文”传递给
(或者其他方法来实现)


我正在考虑定制
CompletionStage
实现,但摆脱“holder”变量似乎是一项艰巨的任务。

不需要有
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 createFuture(){
CompletableFuture proc=CompletableFuture.SupplySync(
() -> {
试一试{
返回新的ProcessBuilder(命令).redirectErrorStream(true).start();
}捕获(IOE异常){
抛出新的未选中异常(e);
}
},调度器);
可完成的未来结果
=proc.thenappyasync(process->PARSER.parse(process.getInputStream()),调度器);
进程同步(进程->{
if(!process.waitFor(持续时间,时间单位,无论持续时间指什么)){
进程。强制销毁();
结果完全异常(
新数据源TimeoutException(new TimeoutException());
}
});
返回结果;
}

如果你想保持超时的未来,也许你认为流程启动时间是重要的,你可以使用

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;
}
public CompletableFuture createFuture(){
CompletableFuture timeout=CreateTimeouture(持续时间);
CompletableFuture proc=CompletableFuture.SupplySync(
() -> {
试一试{
返回新的ProcessBuilder(命令).redirectErrorStream(true).start();
}捕获(IOE异常){
抛出新的未选中异常(e);
}
},调度器);
可完成的未来结果
=proc.thenappyasync(process->PARSER.parse(process.getInputStream()),调度器);
超时。异常情况(t->new DatasourceTimeoutException(t))
.Then接受两者(proc,(x,process)->{
if(process.isAlive()){
进程。强制销毁();
结果:异常完全(x);
}
});
返回结果;
}

我自己也使用了一项数组来模拟Java中合适的闭包

另一个选项是使用带有字段的私有静态类。其优点是,它使目的更加明确,并且对具有大闭包的垃圾收集器(即具有N个字段的对象与长度为1的N个数组的对象)的影响较小。如果您需要在其他方法中关闭相同的字段,它也会很有用

这是一种事实上的模式,甚至超出了
CompletableFuture
的范围,而且早在lambda成为Java中的一种东西之前(ab)就已经使用过,例如匿名类。所以,不要觉得很糟糕,只是Java的发展没有为我们提供合适的闭包(至今?是否?)

如果需要,您可以从
.handle()
中的
CompletableFuture
s返回值,这样您就可以将完成结果完整包装并返回包装器。在我看来,这并不比手动闭包好多少,并补充了一个事实,即您将在将来创建这样的包装器


子类化
CompletableFuture
不是必需的。您对改变其行为不感兴趣,只对向其附加数据感兴趣,这可以通过当前Java的最终变量捕获来实现。也就是说,除非您分析并发现创建这些闭包实际上会以某种方式影响性能,我对此深表怀疑。

第一个片段我不能使用,因为
.waitFor
块,我想消耗可能非常大的
InputStream
(因此
.redirectErrorStream(true)
,我有“错误检测”后来,但那是另一个故事)。第二种方法似乎有点复杂(3个未来,包括
CompletableFuture
),但我会尝试一下,它会阻塞异步操作。这就是“可接受同步”或“完全未来”阶段的全部要点;独立的阶段可以同时运行。这里,
PARSER.parse
process.waitFor
同时在独立的阶段运行。啊,我明白了。那我们就试试等待吧@霍尔格,第一种方法是,每次调用
createFuture
都会阻塞一个超时线程。如果您实现
createTimeoutFuture
中的任一
计时器