Retrieving a task instance scheduled with ScheduledExecutorService
I got a ScheduledExecutorService
for task scheduling in a JEE environment. Some of those task are leaving resources opened when they are interrupted with ScheduledExecutorService.shutdownNow()
(e.g. open files with a third-party lib like Lucene).
I know that a thread may not stop his execution by itself: The must used way to stop a thread is cheeking the interrupt flag and stopping the method execution, and if the thread is block (e.g wait(), sleep(), etc) or if doing some IO operation in a interruptible channel the Thread.interrupt()
will make a InterruptedException
rise. In both cases, the finally block must be executed.
See: http://download.oracle.com/javase/1,5.0/docs/api/java/lang/Thread.html#interrupt%28%29.
Obviously, I already tried to release the resources with a very well implemented finally block in the Task class, but in some environments (e.g. CentOS) the finally block is not executed when the thread is interrupted. And then I found this very cool note in the official Java Documentation:
Note: If the JVM exits while the try or catch code is being executed, then the finally block may not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.
So, what I need is a reference to all the scheduled task in order to implement some public method in the Task classes that force the release of resources. Can I retrieve those references to the task classes from the ScheduledExecutorService
? Or do you have some cool idea to resolve my problem in a better way?
The first solution: Wrap it!
Create a Wrapper class for the ScheduledExecutorService
and add a property like this:
private IdentityHashMap<ScheduledFuture<?>, Runnable> taskList;
With that we can access any Runnable object directly, or by the ScheduledFuture
related to it. For the instantiation of the wrapper, I can get开发者_运维百科 the ScheduledExecutorService
from the Executors.newScheduledThreadPool()
method and pass it to my wrapper.
Another Solution: Extend it!
Extend the ScheduledThreadPoolExecutor
, add the IdentityHashMap property and overwrite all the method that schedules or cancels jobs to add/remove the reference from the Map.
The problem with both solutions?
If the caller of your wrapper or extended class receive a SchedulerFuture<?>
object, cancel the job with the SchedulerFuture<?>.cancel()
method is possible, bypassing your "capsule". With the wrapper you can avoid passing the SchedulerFuture<?>
reference to the caller, but with the extended class you can't (if you create your own methods in the extended class you will get the same result as the wrapper, but in a very confusing way).
The elegant solution: Your own scheduler! Thanks to Kaj for pointing it ...
- Extend the
ScheduledThreadPoolExecutor
to overwrite thedecorateTask()
method - Decorate the
Runnable
with one implementation of aScheduledFuture
interface - Implement one custom
cancel()
method that actually cancels the thread but also manipulates theRunnable
object to force the resource releasing.
Check my blog post for the details and code exemples!!!
What are you scheduling? What does the tasks look like? I find it very hard to believe that the finally block isn't executed. I would guess that it's the tasks that you have scheduled, but that haven't started executing that are leaking resources (since their finally block won't be executed)
Sounds like a really bad VM implementation on the CentOS if it really aren't executing those finally blocks. Haven't heard about that in any other VM implementation.
One option that you can do, instead of referencing all of the scheduled tasks, is to subclass ScheduledThreadPoolExecutor
and override the decorateTask
methods so that they decorate the tasks with your classes, and then intercept the cancel invokation.
精彩评论