Я получил ScheduledExecutorService
за планирование задач в среде JEE. Некоторые из этих задач оставляют ресурсы открытыми, когда они прерываются с помощью ScheduledExecutorService.shutdownNow()
(например, открытые файлы с помощью сторонней библиотеки, такой как Lucene).
Я знаю, что поток не может остановить свое выполнение сам по себе: Обязательным способом остановки потока является проверка флага прерывания и остановка выполнения метода, и если поток заблокирован (например, wait(), sleep() и т. д.) или при выполнении какой-либо операции ввода-вывода в прерываемом канале Thread.interrupt()
увеличится на InterruptedException
. В обоих случаях должен быть выполнен блок finally. См.: http://download.oracle.com/javase/1,5.0/docs/api/java/lang/Thread.html#interrupt%28%29.
Очевидно, я уже пытался освободить ресурсы с очень хорошо реализованным блоком finally в классе Task, но в некоторых средах (например, CentOS) блок finally не выполняется, когда поток прерывается. А потом я нашел эту очень крутую заметку в официальной документации по Java:
Примечание. Если JVM завершает работу во время выполнения кода try или catch, то блок finally может не выполняться. Аналогичным образом, если поток, выполняющий код try или catch, прерывается или уничтожается, блок finally может не выполняться, даже если приложение в целом продолжает работу.
Итак, что мне нужно, так это ссылка на все запланированные задачи, чтобы реализовать какой-то общедоступный метод в классах задач, который принудительно высвобождает ресурсы. Могу ли я получить эти ссылки на классы задач из ScheduledExecutorService
? Или у вас есть классная идея, чтобы решить мою проблему лучше?
Первое решение: завернуть!
Создайте класс Wrapper для ScheduledExecutorService
и добавьте такое свойство:
private IdentityHashMap<ScheduledFuture<?>, Runnable> taskList;
При этом мы можем получить доступ к любому объекту Runnable напрямую или с помощью связанного с ним ScheduledFuture
. Для создания оболочки я могу получить ScheduledExecutorService
из метода Executors.newScheduledThreadPool()
и передать его своей оболочке.
Еще одно решение: расширить его!
Расширьте ScheduledThreadPoolExecutor
, добавьте свойство IdentityHashMap и перезапишите все методы, которые планируют или отменяют задания для добавления/удаления ссылки с карты.
Проблема с обоими решениями?
Если вызывающая сторона вашей оболочки или расширенного класса получает объект SchedulerFuture<?>
, отмена задания методом SchedulerFuture<?>.cancel()
возможна в обход вашей "капсулы". С оберткой вы можете избежать передачи ссылки SchedulerFuture<?>
вызывающей стороне, но с расширенным классом вы не можете (если вы создадите свои собственные методы в расширенном классе, вы получите тот же результат, что и обертка, но очень запутанным образом ).
Элегантное решение: собственный планировщик! Спасибо Каю за указание...
- Расширьте
ScheduledThreadPoolExecutor
, чтобы перезаписать методdecorateTask()
- Украсьте
Runnable
одной реализацией интерфейсаScheduledFuture
- Реализуйте один пользовательский метод
cancel()
, который фактически отменяет поток, но также манипулирует объектомRunnable
, чтобы принудительно освободить ресурс.
Подробности и код см. в моем блоге post. примеры!!!
shutdown()
, послеawaitTermination(some minutes)
и затемshutdownNow()
. Проблема: перестроение индекса Lucene — очень долгая задача (даже часы), поэтому, если перестроение индекса выполняется во время вызоваshutdownNow()
, поток прерывается, но ресурсы не освобождаются. - person ggarciao   schedule 09.08.2011