检索使用 ScheduledExecutorService 计划的任务实例

发布于 2024-11-26 23:22:18 字数 2269 浏览 1 评论 0原文

我在 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 指出...

  1. 扩展 ScheduledThreadPoolExecutor 以覆盖 decorateTask() 方法
  2. 使用 a 的一个实现来装饰 Runnable ScheduledFuture 接口
  3. 实现一个自定义的 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 ...

  1. Extend the ScheduledThreadPoolExecutor to overwrite the
    decorateTask() method
  2. Decorate the Runnable with one implementation of a
    ScheduledFuture interface
  3. Implement one custom cancel() method that actually
    cancels the thread but also manipulates the Runnable object to force
    the resource releasing.

Check my blog post for the details and code exemples!!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

与酒说心事 2024-12-03 23:22:18

你有什么安排?任务是什么样的?我发现很难相信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 the decorateTask methods so that they decorate the tasks with your classes, and then intercept the cancel invokation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文