Всегда ли методы BlockingQueue выдают исключение InterruptedException, когда поток прерывается?

В одном из моих приложений Java 6 у меня есть поток, который снабжает основной поток данными, а также предварительно выбирает больше записей из БД. Он использует очередь ArrayBlockingQueue в качестве буфера FIFO, а его основной цикл представляет собой что-то вроде следующих строк:

while (!Thread.interrupted()) {
    if (source.hasNext()) {
        try {
            queue.put(source.next())
        } catch (InterruptedException e) {
            break;
        }
    } else {
        break;
    }
}

Существует код, который выполняет небольшую очистку после завершения цикла, например отравляет очередь и освобождение любых ресурсов, но это почти все.

В существующем виде отсутствует прямая связь между основным потоком и питающим потоком: питающий поток настроен с правильными параметрами, а затем оставлен сам по себе, используя очередь блокировки для контролировать поток данных.

Проблема возникает, когда главному потоку необходимо отключить податчик при заполнении очереди. Поскольку прямого канала управления нет, метод выключения использует интерфейс Thread для interrupt () потока питателя. К сожалению, в большинстве случаев поток питателя остается заблокированным в put () , несмотря на то, что он прерван - исключение не генерируется.

Из краткого ознакомления с документацией interrupt () и исходным кодом реализации очереди мне кажется, что довольно часто put () блокируются без использования каких-либо средств прерывания. JVM. В частности, в моей текущей JVM (OpenJDK 1.6b22) он блокирует собственный метод sun.misc.Unsafe.park () . Возможно, он использует спин-блокировку или что-то еще, но в любом случае это, похоже, подпадает под следующий случай :

Если ни одно из предыдущих условий не выполняется, то будет установлен статус прерывания этого потока.

Флаг состояния установлен, но поток все еще заблокирован в put () и не выполняет дальнейших итераций, чтобы можно было проверить этот флаг. Результат? Зомби-нить, которая просто не умрет !

  1. Я правильно понимаю эту проблему, или я что-то упускаю?

  2. Каковы возможные подходы к решению этой проблемы? Сейчас я могу думать только о двух решениях:

    a.Вызов poll () несколько раз в очереди, чтобы разблокировать питающий поток: Уродливо и не очень надежно из того, что я видел, но он в основном работает.

    б. Используйте метод offer () с тайм-аутом вместо put () , чтобы позволить потоку проверить свое состояние прерывания в течение приемлемого периода времени.

Если я чего-то не упускаю, это несколько недокументированное предостережение относительно реализаций BlockingQueue в Java. Там кажется некоторые признаки этого, когда документация, например, предлагает отравить очереди для завершения рабочего потока, но я не могу найти явной ссылки.

РЕДАКТИРОВАТЬ:

Хорошо, есть более радикальное изменение решения (а) выше: ArrayBlockingQueue.clear () . Я думаю, это всегда должно работать, даже если это не совсем определение элегантности ...

7
задан thkala 13 February 2012 в 01:57
поделиться