Обычным способом является прерывание потоков, но это, конечно же, требует правильной обработки прерываний.
Это означает, что улавливать и обрабатывать InterruptedException
s надлежащим образом вокруг методов блокировки, а также регулярно проверять (и действовать) флаг interrupted
.
В спецификации API или языка нет ничего, что связывает прерывание с какой-либо определенной семантикой отмены, но на практике использование прерывания для чего угодно, кроме отмены, является хрупким и трудно выдерживается в больших приложениях. [...]
Прерывание - это, как правило, самый разумный способ осуществить отмену.
Говорит Java Concurrency in Practice в разделе 7.1.1. Пример обработки прерывания правильно, от этого же (это производитель нить, а не потребитель, но разница незначительна в текущем контексте):
class PrimeProducer extends Thread {
private final BlockingQueue<BigInteger> queue;
PrimeProducer(BlockingQueue<BigInteger> queue) {
this.queue = queue;
}
public void run() {
try {
BigInteger p = BigInteger.ONE;
while (!Thread.currentThread().isInterrupted())
queue.put(p = p.nextProbablePrime());
} catch (InterruptedException consumed) {
/* Allow thread to exit */
}
}
public void cancel() { interrupt(); }
}
Альтернативным решением было бы установить параметр времени ожидания poll
достаточно низкий, так что поток просыпается регулярно и может заметить прерывания достаточно быстро. Тем не менее, я считаю, что всегда хорошая практика обрабатывать InterruptedException явно в соответствии с вашей конкретной политикой аннулирования потока.
Зачем вам это делать? Для BlockingQueue необходимо решить, какой поток может возобновиться.Основываясь на реализации BlockingQueue, в состоянии очереди нет надежного предположения. – cafebabe
@bfoo, я предполагаю, что цель состоит в том, чтобы прекратить потоки грациозно, когда больше нечего делать. –