M
mievlecl
Hello. I am working with the java.util.concurrent API introduced in
Java 1.5. Specifically, I will talk about Executors, ExecutorServices
and Futures.
So far, I have gotten a lot of use out of the API, and used it on a
number of projects. However, I am consistently running into a problem
for which I have not found a good solution.
Callables submitted to an ExecutorService return a reference to a
Future object. This future object gives you some modest ways to
interact with the task: a method to cancel the task (cancel), methods
to query the status of the task (isDone/isCancelled), and methods to
get the result of the task's processing (get).
The problem is as follows:
Say you wish to cancel some long-running task that you know is queued
for execution. Let's say you do this with, say:
future.cancel(true); // true allows the executor to use an interrupt
to help cancel the task
The trouble here, is, now that we have cancelled the task, we have no
way of determining if the task was cancelled before running, or if it
was cancelled while running.
Let us say that there's something we always need to do after this
task. Let's call "something" a method: "myCleanup()".
By "after this task," I mean we always need to this "myCleanup()"
whether the task ran to completion or was cancelled. So, there should
be a one-to-one relationships between enqueues of the task, and calls
to "myCleanup()".
For the sake of serial execution, we put the call to "myCleanup()" in
the task code itself. So the core of the task code looks something
like:
performWhateverThisTaskDoes();
cleanup();
However, it is possible that the call to --
future.cancel(true);
-- will prevent this task from ever being executed. This is
especially true if, for example, our executor has a sizable task
backlog in its work queue. That's OK. We asked for the task to be
cancelled, and we got it.
The trouble comes when you realize (or already know) that --
future.cancel(true);
-- does not always stop the task from executing. If the task is "in
the process of executing," cancelling it will actually normally do
nothing -- the task will happily run to completion. This is OK and I
have come to terms with this behavior.
The crux of the problem is there's no way to figure out what happened
-- what state the task as in, when it was cancelled. I have no idea
if I should expect that the task is "in progress" and will eventually
call myCleanup() for me, or if it is still "in queue," and will never
execute, because it's been cancelled before any executor even set eyes
on it.
Assume, for example purposes, that because of the nature of the
myCleanup() method, it is undesirable or incorrect to call it twice
per task enqueued.
This leaves me in a tricky spot: I've cancelled the task, but I have
no way of knowing if the task was cancelled "pre-execution" or "mid-
execution." It's important for me to know, so that I can know whether
"myCleanup()" needs to be called manually, or if it will be handled by
the task.
To me, it seems a fairly natural thing to be able to cancel tasks. It
also seems like a fairly useful piece of information to know if that
task was cancelled before beginning, or was cancelled while running --
since the two scenarios can leave the software in very different
states. Yet, as far as I can see, there is no way to determine this
information.
You may suggest to me the isDone/isCancelled Future API methods.
Please note that there is actually no way to determine whether a task
actually ran or was cancelled before running by using the information
retrieved from these methods. Nor is the return value from
Future.cancel() informative in these situations.
I have also tried extending FutureTask and overriding #done(). This
method is not called if your task is cancelled before execution. As
is it is not possible to efficiently respond to the "absence" of a
method call, this is not a very good way to determine if a task will
or will not run.
Any ideas?
Java 1.5. Specifically, I will talk about Executors, ExecutorServices
and Futures.
So far, I have gotten a lot of use out of the API, and used it on a
number of projects. However, I am consistently running into a problem
for which I have not found a good solution.
Callables submitted to an ExecutorService return a reference to a
Future object. This future object gives you some modest ways to
interact with the task: a method to cancel the task (cancel), methods
to query the status of the task (isDone/isCancelled), and methods to
get the result of the task's processing (get).
The problem is as follows:
Say you wish to cancel some long-running task that you know is queued
for execution. Let's say you do this with, say:
future.cancel(true); // true allows the executor to use an interrupt
to help cancel the task
The trouble here, is, now that we have cancelled the task, we have no
way of determining if the task was cancelled before running, or if it
was cancelled while running.
Let us say that there's something we always need to do after this
task. Let's call "something" a method: "myCleanup()".
By "after this task," I mean we always need to this "myCleanup()"
whether the task ran to completion or was cancelled. So, there should
be a one-to-one relationships between enqueues of the task, and calls
to "myCleanup()".
For the sake of serial execution, we put the call to "myCleanup()" in
the task code itself. So the core of the task code looks something
like:
performWhateverThisTaskDoes();
cleanup();
However, it is possible that the call to --
future.cancel(true);
-- will prevent this task from ever being executed. This is
especially true if, for example, our executor has a sizable task
backlog in its work queue. That's OK. We asked for the task to be
cancelled, and we got it.
The trouble comes when you realize (or already know) that --
future.cancel(true);
-- does not always stop the task from executing. If the task is "in
the process of executing," cancelling it will actually normally do
nothing -- the task will happily run to completion. This is OK and I
have come to terms with this behavior.
The crux of the problem is there's no way to figure out what happened
-- what state the task as in, when it was cancelled. I have no idea
if I should expect that the task is "in progress" and will eventually
call myCleanup() for me, or if it is still "in queue," and will never
execute, because it's been cancelled before any executor even set eyes
on it.
Assume, for example purposes, that because of the nature of the
myCleanup() method, it is undesirable or incorrect to call it twice
per task enqueued.
This leaves me in a tricky spot: I've cancelled the task, but I have
no way of knowing if the task was cancelled "pre-execution" or "mid-
execution." It's important for me to know, so that I can know whether
"myCleanup()" needs to be called manually, or if it will be handled by
the task.
To me, it seems a fairly natural thing to be able to cancel tasks. It
also seems like a fairly useful piece of information to know if that
task was cancelled before beginning, or was cancelled while running --
since the two scenarios can leave the software in very different
states. Yet, as far as I can see, there is no way to determine this
information.
You may suggest to me the isDone/isCancelled Future API methods.
Please note that there is actually no way to determine whether a task
actually ran or was cancelled before running by using the information
retrieved from these methods. Nor is the return value from
Future.cancel() informative in these situations.
I have also tried extending FutureTask and overriding #done(). This
method is not called if your task is cancelled before execution. As
is it is not possible to efficiently respond to the "absence" of a
method call, this is not a very good way to determine if a task will
or will not run.
Any ideas?