检索使用 ScheduledExecutorService 计划的任务实例
我在 JEE 环境中获得了用于任务调度的 ScheduledExecutorService
。其中一些任务在被 ScheduledExecutorService.shutdownNow()
中断时会保留打开的资源(例如,使用 Lucene 等第三方库打开文件)。
我知道线程可能不会自行停止执行:停止线程的必须使用的方法是检查中断标志并停止方法执行,并且如果线程被阻塞(例如 wait()、sleep() 等)或者,如果在可中断通道中执行某些 IO 操作,Thread.interrupt()
将引发 InterruptedException
。在这两种情况下,都必须执行finally 块。 请参阅: http:// /download.oracle.com/javase/1,5.0/docs/api/java/lang/Thread.html#interrupt%28%29。
显然,我已经尝试在Task类中使用很好实现的finally块来释放资源,但是在某些环境(例如CentOS)中,当线程中断时,finally块不会被执行。然后我在官方 Java 文档中发现了这个非常酷的注释:
注意:如果 JVM 在 try 或 catch 代码执行时退出, 那么finally块可能不会执行。同样,如果线程 执行try或catch代码被中断或杀死,finally 即使应用程序作为一个整体,块也可能无法执行 继续。
因此,我需要的是对所有计划任务的引用,以便在任务类中实现一些强制释放资源的公共方法。我可以从 ScheduledExecutorService
检索对任务类的引用吗?或者您有什么好主意可以更好地解决我的问题吗?
第一个解决方案:包装它!
为 ScheduledExecutorService
创建一个 Wrapper 类并添加如下属性:
private IdentityHashMap<ScheduledFuture<?>, Runnable> taskList;
有了它,我们可以直接访问任何 Runnable 对象,或者通过 >ScheduledFuture
与之相关。对于包装器的实例化,我可以从 Executors.newScheduledThreadPool()
方法获取 ScheduledExecutorService
并将其传递给我的包装器。
另一个解决方案:扩展它!
扩展ScheduledThreadPoolExecutor
,添加IdentityHashMap属性并覆盖所有调度或取消作业的方法,以在Map中添加/删除引用。
两种解决方案都有问题?
如果包装器或扩展类的调用者收到 SchedulerFuture
对象,请使用 SchedulerFuture 取消作业;.cancel()
方法是可能的,绕过你的“胶囊”。使用包装器,您可以避免将 SchedulerFuture
引用传递给调用者,但使用扩展类则不能(如果您在扩展类中创建自己的方法,您将得到相同的结果)结果作为包装器,但以一种非常令人困惑的方式)。
优雅的解决方案:您自己的调度程序!感谢 Kaj 指出...
- 扩展
ScheduledThreadPoolExecutor
以覆盖decorateTask()
方法 - 使用 a 的一个实现来装饰
Runnable
ScheduledFuture
接口 - 实现一个自定义的
cancel()
方法,该方法实际上 取消线程,但也操纵Runnable
对象来强制 资源释放。
查看我的博客 帖子 了解详细信息和代码例子!!!
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!!!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
你有什么安排?任务是什么样的?我发现很难相信finally 块没有被执行。我猜想这是您已安排但尚未开始执行的任务正在泄漏资源(因为它们的finally块不会被执行)
听起来像是CentOS上的一个非常糟糕的VM实现,如果它确实不是的话执行这些finally 块。在任何其他虚拟机实现中都没有听说过这一点。
您可以做的一种选择是,继承
ScheduledThreadPoolExecutor
并重写decorateTask
方法,以便它们用您的类装饰任务,而不是引用所有计划任务。然后拦截取消调用。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 thedecorateTask
methods so that they decorate the tasks with your classes, and then intercept the cancel invokation.