我想就以下问题和解决方案(潜在的)获得您的反馈。

假设有一个Runnable任务,该任务采样CPU使用率并将其写入CSV文件。此任务不会返回任何结果。需要将其安排为永远定期执行。如果用户取消任务,我们将关闭文件编写器,并且用户可以下载CSV文件。

当您将这样的任务提交给ListeningScheduledExecutorService(如下所示)时,您将获得一个ListenableScheduledFuture,它代表任务的结果。

ListenableScheduledFuture<Void> future = (ListenableScheduledFuture<Void>) scheduledExecutorService
            .scheduleAtFixedRate(task, initialDelay, interval, timeUnit);


我们注册了一个将来的回调,以便在取消任务后可以下载CSV文件:也就是说,我们需要将ListenableScheduledFuture转换为ListenableScheduledFuture。

Function<Void, File> transformFunc = new Function<Void, File>()
{
    @Override
    public File apply(Void arg0)
    {
        return file;
    }
};

ListenableScheduledFuture<File> futureToListen = (ListenableScheduledFuture<File>) Futures.transform(future, transformFunc);

// register a callback to the ListenableScheduledFuture<File>
Futures.addCallback(futureToListen, callback, executorService);


注意:根据Guava API DOC 1,取消futureToListen应该导致我们的任务被取消。

问题:


由于任务结果无效,因此如上所示将其强制转换为ListenableScheduledFuture是否有意义?
从语义上讲,将将无效返回的任务的未来转换为某种类型的未来是否有意义?
有没有更好,更优雅的解决方案?使用Java8的CompletableFuture会更好吗?

最佳答案

transform可能不会做您想要的。您提到取消futureToListen将取消future,这是事实。问题是储备金也是如此:取消future时,futureToListen也将取消。这将意味着它永远不会计算其结果。

要解决此问题,您有两种选择:


如果您不希望能够取消future,则将futureToListen更改为SettableFuture。然后,取消future的任何人也会呼叫futureToListen.set(file)
如果您确实希望能够取消future,则将futureToListen更改为(a)公开AbstractFutureset()(以便您可以执行我在(1)中建议的操作)和(b)实现interruptTask()呼叫future.cancel()


还有其他可能的聪明的解决方案,涉及在future未成功完成时触发工作,但是我不确定我是否建议这样做。我个人对使用“ Future已取消”感到有些紧张,因为它表示“工作已成功完成”。例如,这是因为Future也可以通过执行程序关闭而取消。 (另一个原因:您需要小心区分“在执行过程中引发异常”和“被取消”。)由于这个原因,我建议的解决方案设置为直接调用set()而不是触发任何取消未来。

07-26 03:14