取消Future并仍然检索其返回值

时间:2017-01-25 17:36:30

标签: java multithreading

我有一个记录器类,每n毫秒轮询数据源中的值。整个过程应该异步运行并返回包含记录数据的集合。

我的想法是拥有一个带有单个线程的ExecutorService和一个执行数据轮询的Callable。应该可以调用callable,因此调用者不必等待任何Thread.sleep调用完成。要中断他可调用我在ExecutorService返回的Future上调用Future.cancel(true)

class Recorder {
    private DataSource source;
    private ExecutorService executor;
    private Future<List<MyData>> dataFuture;

    private class FixedPollRateCallable {
        private DataSource source;
        private long pollrate;

        public FixedPollrateCallable(DataSource source, long pollrate) {
            this.source = source;
            this.pollrate = pollrate;
        }

        public List<MyData> call() throws Exception {
            List<MyData> dataList = new ArrayList<>();
            while(!Thread.currentThread().isInterrupted()) {
                dataList.add(source.getData());
                try {
                    Thread.sleep(pollrate);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            return dataList;
        }
    }

    public Recorder(DataSource source) {
        this.source = source;
        this.executor = Executors.newSingleThreadExecutor();
    }

    public void startRecording(long pollrate) {
        if(dataFuture != null && !dataFuture.isDone())
            throw new RecordingException();

        dataFuture = executor.submit(new FixedPollRateCallable(source, pollrate);
    }

    public void stopRecording() {
        dataFuture.cancel(true);
    }

    public List<MyData> getRecordedData() throws InterruptedException, ExecutionException {
        return dataFuture.get();
    }
}

但是在调用这样的事情的时候:

recorder.startRecording();
recorder.stopRecording();
recorder.getRecordedData();

我在java.util.concurrent.CancellationException电话上收到了recorder.getRecordedData()

我想除了中断线程外,cancel(true)还会设置取消标记,因此对Future.get()的调用将始终失败。

有没有办法,或者你知道一个更好的替代品,允许我打断一个线程,突破Thread.sleep()并仍然能够返回一个值?

1 个答案:

答案 0 :(得分:3)

嗯,你问的是概念上没有意义的东西:

让我们引用get()的javadoc:

抛出:CancellationException - 如果计算被取消

对于cancel(),它说:

此方法返回后,对isDone()的后续调用将始终返回true。如果此方法返回true,则对isCancelled()的后续调用将始终返回true。

换句话说:你可能需要一些其他类型的机制。例如,您可以将Runable更改为推送已收集的信息,而不是将其返回。

换句话说:在某些时候,不要使用Future来获取所有数据;为什么不直接在抵达时将所有数据发送到“安全水槽”?你的方法是民意调查;但显然:投票和打断不是齐头并进的!

编辑:现在你创建你的记录器:

 public Recorder(DataSource source)

所以你构建了从source接收“数据”。

可以是一个简单的解决方案
 public Recorder(DataSource source, List<MyData> sink)

而不是在您的Runable中保留本地dataList ...您只需附加到提供给记录器的sink

我使用“安全”这个词只是指多个线程开始更新相同列表时需要注意的事实。