Получение экземпляра задачи, запланированной с помощью ScheduledExecutorService

Я получил 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<?> вызывающей стороне, но с расширенным классом вы не можете (если вы создадите свои собственные методы в расширенном классе, вы получите тот же результат, что и обертка, но очень запутанным образом ).

Элегантное решение: собственный планировщик! Спасибо Каю за указание...

  1. Расширьте ScheduledThreadPoolExecutor, чтобы перезаписать метод decorateTask()
  2. Украсьте Runnable одной реализацией интерфейса ScheduledFuture
  3. Реализуйте один пользовательский метод cancel(), который фактически отменяет поток, но также манипулирует объектом Runnable, чтобы принудительно освободить ресурс.

Подробности и код см. в моем блоге post. примеры!!!


person ggarciao    schedule 29.07.2011    source источник
comment
Вы подозреваете, что виртуальная машина выходит из строя? и не запускать блок finally? Если да, то почему бы не выйти из виртуальной машины только тогда, когда все задачи были отменены (т. е. внедрить ожидание завершения на исполнителе)? Это может быть просто состояние гонки..?   -  person Toby    schedule 04.08.2011
comment
Это происходит, когда приложение tomcat закрыто, а библиотека Lucene является общим ресурсом на сервере. Итак, все потоки приложения отменены, но виртуальная машина все еще жива, а библиотека по-прежнему доступна для других веб-приложений. С другой стороны, когда мы закрываем приложение, мы делаем: shutdown(), после awaitTermination(some minutes) и затем shutdownNow(). Проблема: перестроение индекса Lucene — очень долгая задача (даже часы), поэтому, если перестроение индекса выполняется во время вызова shutdownNow(), поток прерывается, но ресурсы не освобождаются.   -  person ggarciao    schedule 09.08.2011
comment
хм, я бы назвал это ошибкой с lucene! потоки должны правильно реагировать на прерывание IMO (они должны реализовывать правильную политику прерывания). Вам придется очень много работать, чтобы обойти это, кажется...   -  person Toby    schedule 12.08.2011


Ответы (1)


Что вы планируете? Как выглядят задания? Мне очень трудно поверить, что блок finally не выполняется. Я предполагаю, что это задачи, которые вы запланировали, но которые еще не начали выполняться, что приводит к утечке ресурсов (поскольку их блок finally не будет выполнен)

Звучит как очень плохая реализация виртуальной машины в CentOS, если она действительно не выполняет эти блоки finally. Не слышал об этом ни в одной другой реализации виртуальной машины.

Один из вариантов, который вы можете сделать вместо того, чтобы ссылаться на все запланированные задачи, состоит в том, чтобы создать подкласс ScheduledThreadPoolExecutor и переопределить методы decorateTask, чтобы они украсили задачи вашими классами, а затем перехватить вызов отмены.

person Kaj    schedule 29.07.2011
comment
Моя периодическая запланированная задача обновляет индекс Lucene (lucene.apache.org/java/docs/index .html). Для нас проблема с блоком finally невероятна, но наша диагностика довольно хороша: в нашей CentOS блок finally никогда не достигается (после прерывания потока). Спасибо за совет по расширению исполнителя пула. Я проверю, как это сделать. - person ggarciao; 29.07.2011
comment
@ggarciao - вы уверены, что прерывание происходит в блоке finally? и вы не делаете ничего похожего на Thread.stop()? обычное прерывание потока должно всегда попадать в блок finally. Кроме того, возможно ли, что вы делаете что-то внутри блока finally, что чувствительно к прерыванию потока и, следовательно, пропускает остальную часть блока finally? - person jtahlborn; 29.07.2011
comment
Не совсем, я закрываю некоторые программы чтения, записи и каталоги Lucene, а также эти операции находятся внутри других блоков try-catch. - person ggarciao; 02.08.2011