Simultaneidade Java - Políticas de Interrupção
Estou lendo Java Concurrency in Practice . Na seção Políticas de Interrupção no capítulo
Cancelamento e desligamento
é mencionado
Uma tarefa não deve assumir nada sobre a política de interrupção de seu thread em execução, a menos que seja explicitamente projetada para ser executada em um serviço que tenha uma política de interrupção específica. Quer uma tarefa interprete a interrupção como cancelamento ou execute alguma outra ação na interrupção, ela deve tomar cuidado para preservar o status de interrupção do thread em execução. Se não for propagar InterruptedException para seu chamador, ele deve restaurar o status de interrupção após capturar InterruptionException: Thread.currentThread().interrupt()
Então, tentei brincar com a amostra de listagem para entender. Mas estou confuso com a saída.
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());
}
}
}
método principal##
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;
}
Resultado:
// 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
Pergunta
Apenas adicionando TimeUnit.SECONDS.sleep(5) no thread principal da classe principal. O status de interrupção do thread em execução (ou seja, gerador) está sendo redefinido. Se eu comentar o método TimeUnit.SECONDS.sleep(5), nesse caso, o status de interrupção será retido. Por que isso está acontecendo e como?
No livro, é mencionado que um thread deve ser interrompido apenas por seu proprietário. Aqui no exemplo acima, quem é o proprietário? Acho que é o thread do método principal.
Respostas
Ao adicionar TimeUnit.SECONDS.sleep(5), você está dando tempo suficiente para o encadeamento terminar.
Quando um thread termina, seu sinalizador de interrupção é limpo.
Isso não está documentado na especificação, mas é o que acontece. Veja, por exemplo , este relatório de bug :
Não há nenhuma especificação sendo violada aqui, então fiz uma solicitação de aprimoramento em vez de um bug. Indiscutivelmente, a falta de especificação é um bug - especificamos intencionalmente que "a interrupção após o término não precisa afetar" para lidar com o fato de que o estado da interrupção é armazenado na VM e não existe mais depois que um encadeamento é encerrado. No entanto, deixamos de refletir isso na especificação Thread.isInterrupted.
Sem o extra sleep, suspeito que, em teoria, você poderia ver ambos truee falseinterromper o status porque há uma condição de corrida, mas é muito mais provável que você veja truegraças ao agendamento de encadeamento. A janela de tempo em que o status da interrupção é falso, entre a exceção lançada e a restauração do status da interrupção no bloco catch, é incrivelmente pequena.
Apenas adicionando TimeUnit.SECONDS.sleep(5) no thread principal da classe principal. O status de interrupção do thread em execução (ou seja, gerador) está sendo redefinido. Se eu comentar o método TimeUnit.SECONDS.sleep(5), nesse caso, o status de interrupção será retido. Por que isso está acontecendo e como?
Você não está usando nenhum mecanismo de sincronização (além da fila de bloqueio) entre o thread principal e CorrectPrimeProducerquando o thread principal imprime o status - o CorrectPrimeProducerpode não ter preservado o status interrompido ainda (executando catchinstruções de bloco), portanto, você obtém falseo resultado.
Quando você adiciona sleepao principal Thread, você apenas aumenta a possibilidade de que o CorrectPrimeProducerthread preserve o status de interrupção invocando catchinstruções de bloco antes que o thread principal tente imprimir seu status. É por isso que imprime true.
No livro, é mencionado que um thread deve ser interrompido apenas por seu proprietário. Aqui no exemplo acima, quem é o proprietário? Acho que é o thread do método principal.
Nesse caso, você é o proprietário (o proprietário é o código que cria o encadeamento) do CorrectPrimeProducerencadeamento, portanto, você decide o que a interrupção significa para ele. Por exemplo, você pode recriá-lo se foi interrompido (isso acontece, por exemplo, para Threads de conjuntos de encadeamentos java por padrão).