Параллелизм Java - политики прерывания
Я читаю Java Concurrency на практике . В разделе Политика прерывания в главе
Отмена и остановка
Упомянутое
Задача не должна ничего предполагать о политике прерывания выполняемого ею потока, если она явно не предназначена для работы в рамках службы, имеющей определенную политику прерывания. Независимо от того, интерпретирует ли задача прерывание как отмену или предпринимает какие-либо другие действия при прерывании, она должна позаботиться о сохранении статуса прерывания выполняющегося потока. Если он не собирается распространять InterruptedException своему вызывающему, он должен восстановить статус прерывания после перехвата InterruptionException: Thread.currentThread (). Interrupt ()
Поэтому я попытался поиграть с образцом листинга, чтобы понять. Но меня смущает вывод.
PrimeProducer
public class CorrectPrimeProducer extends Thread {
private final BlockingQueue<BigInteger> queue;
public CorrectPrimeProducer(BlockingQueue<BigInteger> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" interrupt status in producer:" + Thread.currentThread().isInterrupted());
BigInteger p = BigInteger.ONE;
while (!Thread.currentThread().isInterrupted()) {
queue.put(p = p.nextProbablePrime());
}
} catch (InterruptedException e) {
/* Allow thread to exit */
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().getName()+" interrupt status in producer catch:" + Thread.currentThread().isInterrupted());
}
}
}
основной метод ##
public static void main(String[] args) throws InterruptedException {
BlockingQueue<BigInteger> primes = new LinkedBlockingQueue<>();
CorrectPrimeProducer generator = new CorrectPrimeProducer(primes);
generator.start();
try {
while (needMorePrimes()) {
consume(primes.take());
}
} finally {
generator.interrupt();
}
TimeUnit.SECONDS.sleep(5);
System.out.println(generator.getName()+" interrupt status in main:"+generator.isInterrupted());
}
//do something
private static void consume(BigInteger take) {
System.out.println(take);
}
private static int counter = 1;
private static boolean needMorePrimes() {
counter++;
if(counter == 10){
// after counter reaches 10 return false
return false;
}
return true;
}
Выход:
// when TimeUnit.SECONDS.sleep(5); in main class is not commented
Thread-0 interrupt status in producer:false
2
3
5
7
11
13
17
19
Thread-0 interrupt status in producer catch:true
Thread-0 interrupt status in main:false
//When TimeUnit.SECONDS.sleep(5); in main class is commented
Thread-0 interrupt status in producer:false
2
3
5
7
11
13
17
19
Thread-0 interrupt status in main:true
Thread-0 interrupt status in producer catch:true
Вопрос
Просто добавив TimeUnit.SECONDS.sleep (5) в основной поток в основном классе. Состояние прерывания исполняемого потока (т. Е. Генератора) сбрасывается. Если я прокомментирую метод TimeUnit.SECONDS.sleep (5), тогда статус прерывания сохраняется. Почему это происходит и как?
В книге указано, что ветка должна прерываться только ее владельцем. Кто в приведенном выше примере владелец? Я считаю его основным методом потока.
Ответы
Добавляя, TimeUnit.SECONDS.sleep(5)вы даете достаточно времени для завершения потока.
Когда поток завершается, его флаг прерывания сбрасывается.
Это не задокументировано в спецификации, но это то, что происходит. См., Например, этот отчет об ошибке :
Здесь не нарушается никакая спецификация, поэтому я сделал это скорее как запрос на улучшение, чем за ошибку. Возможно, отсутствие спецификации является ошибкой - мы намеренно указали, что «прерывание после завершения не должно иметь никакого влияния», чтобы иметь дело с тем фактом, что состояние прерывания сохраняется в виртуальной машине и больше не существует после завершения потока. Однако мы не учли это в спецификации Thread.isInterrupted.
Без дополнительных sleep, я подозреваю , что в теории можно было увидеть как trueи falseпрерывание состояния , потому что это состояние гонки, но это гораздо более вероятно , что вы увидите , trueблагодаря нитку планирования. Временной интервал, в котором состояние прерывания является ложным, между генерируемым исключением и восстановлением состояния прерывания в блоке catch, невероятно мал.
Просто добавив TimeUnit.SECONDS.sleep (5) в основной поток в основном классе. Состояние прерывания исполняемого потока (т. Е. Генератора) сбрасывается. Если я прокомментирую метод TimeUnit.SECONDS.sleep (5), тогда статус прерывания сохраняется. Почему это происходит и как?
Вы не используете какой-либо механизм синхронизации (кроме очереди блокировки) между основным потоком и CorrectPrimeProducerпоэтому, когда основной поток печатает статус - CorrectPrimeProducerвозможно, он еще не сохранил прерванный статус (путем выполнения catchинструкций блока), таким образом, вы получаете falseв результате.
Когда вы добавляете sleepк основному, Threadвы просто увеличиваете вероятность того, что CorrectPrimeProducerпоток сохранит статус прерывания, вызывая catchинструкции блока до того, как основной поток попытается распечатать его статус. Вот почему он печатает true.
В книге указано, что ветка должна прерываться только ее владельцем. Кто в приведенном выше примере владелец? Я считаю его основным методом потока.
В этом случае вы являетесь владельцем (владельцем является код, который создает поток) CorrectPrimeProducerпотока, поэтому вы решаете, что для него означает прерывание. Например, вы можете воссоздать его, если он был прерван (это происходит, например, для Threads из пулов потоков Java по умолчанию).