Reputation: 2731
I have a recorder class that polls for values from a data source every n milliseconds. The whole this is supposed to run asynchronously and return a collection containing the recorded data.
My Idea was to have an ExecutorService with a single thread and a Callable that does the data polling. The callable is supposed to be interrupted, so the caller does not have to wait for any Thread.sleep
calls to finish. To interrupt he Callable i call Future.cancel(true)
on the Future returned from the ExecutorService:
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();
}
}
But when calling the whole thing like this:
recorder.startRecording();
recorder.stopRecording();
recorder.getRecordedData();
I get a java.util.concurrent.CancellationException
on the recorder.getRecordedData()
call.
I guess that besides interrupting the Thread, cancel(true)
also sets a cancellation flag, so calls to Future.get()
will always fail.
Is there any way around that, or do you know about a better alternative that allows me to interrupt a Thread, breaking out of Thread.sleep()
and still being able to return a value?
Upvotes: 2
Views: 679
Reputation: 140427
Well, you ask for something that conceptually does not make sense:
Lets quote the javadoc for get():
Throws: CancellationException - if the computation was cancelled
And for cancel()
it says:
After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true.
In other words: you probably need some other kind of mechanism here. For example, you could change your Runable to push the already collected information instead of returning it.
In other words: instead of using a Future to get all data at some point; why don't you simply send all data to a "safe sink" directly upon arrival? Your approach is to poll; but obviously: polling and interrupting do not go hand in hand!
Edit: right now you create your Recorder like this:
public Recorder(DataSource source)
so you built to receive "data" from source
. An easy solution could be to have
public Recorder(DataSource source, List<MyData> sink)
as well.
And instead of keeping a local dataList
in your Runable ... you simply append to the sink
provided to the recorder.
Me using the word "safe" simply refers to the fact that need to pay attention when multiple threads start updating the same list for example.
Upvotes: 3