Concorrenza Java - Criteri di interruzione
Sto leggendo Java Concurrency in Practice . Nella sezione Politiche di interruzione nel capitolo
Cancellazione e spegnimento
È menzionato
Un'attività non dovrebbe presupporre nulla sulla politica di interruzione del suo thread in esecuzione a meno che non sia esplicitamente progettata per essere eseguita all'interno di un servizio che ha una politica di interruzione specifica. Se un'attività interpreta l'interruzione come annullamento o intraprende un'altra azione in caso di interruzione, dovrebbe fare attenzione a preservare lo stato di interruzione del thread in esecuzione. Se non propagherà InterruptedException al suo chiamante, dovrebbe ripristinare lo stato di interruzione dopo aver rilevato InterruptionException: Thread.currentThread().interrupt()
Quindi ho provato a giocare con un esempio di elenco per capire. Ma sono confuso con l'output.
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());
}
}
}
metodo principale##
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;
}
Produzione:
// 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
Domanda
Semplicemente aggiungendo TimeUnit.SECONDS.sleep(5) nel thread principale nella classe principale. Lo stato di interruzione del thread in esecuzione (ovvero, generatore) viene reimpostato. Se commento il metodo TimeUnit.SECONDS.sleep(5), in tal caso viene mantenuto lo stato di interruzione. Perché sta accadendo e come?
Nel libro è menzionato Un thread dovrebbe essere interrotto solo dal suo proprietario. Qui nell'esempio sopra chi è il proprietario? Penso che sia il thread del metodo principale.
Risposte
Aggiungendo TimeUnit.SECONDS.sleep(5)stai dando abbastanza tempo per terminare il thread.
Quando un thread termina, il suo flag di interruzione viene cancellato.
Questo non è documentato nelle specifiche, ma è quello che succede. Vedi ad esempio questa segnalazione di bug :
Non vi è alcuna specifica violata qui, quindi ho reso questa una richiesta di miglioramento piuttosto che un bug. Probabilmente la mancanza di specifiche è un bug: abbiamo intenzionalmente specificato che "l'interruzione dopo la terminazione non deve avere alcun effetto" per far fronte al fatto che lo stato di interruzione è memorizzato nella VM e non esiste più una volta che un thread è terminato. Tuttavia, abbiamo trascurato di rifletterlo nelle specifiche Thread.isInterrupted.
Senza l'extra sleep, sospetto che in teoria potresti vedere entrambi truee falseinterrompere lo stato perché c'è una race condition, ma è molto più probabile che tu veda truegrazie alla pianificazione dei thread. La finestra temporale in cui lo stato di interruzione è falso, tra l'eccezione lanciata e il ripristino dello stato di interruzione nel blocco catch, è incredibilmente piccola.
Semplicemente aggiungendo TimeUnit.SECONDS.sleep(5) nel thread principale nella classe principale. Lo stato di interruzione del thread in esecuzione (ovvero, generatore) viene reimpostato. Se commento il metodo TimeUnit.SECONDS.sleep(5), in tal caso viene mantenuto lo stato di interruzione. Perché sta accadendo e come?
Non stai utilizzando alcun meccanismo di sincronizzazione (a parte la coda di blocco) tra il thread principale e CorrectPrimeProducerquindi quando il thread principale stampa lo stato - CorrectPrimeProducerpotrebbe non aver ancora preservato lo stato interrotto (eseguendo catchle istruzioni di blocco), quindi ottieni falsecome risultato.
Quando aggiungi sleepal main Thread, aumenti semplicemente la possibilità che il CorrectPrimeProducerthread preservi lo stato di interruzione invocando catchle istruzioni di blocco prima che il thread principale tenti di stampare il suo stato. Ecco perché stampa true.
Nel libro è menzionato Un thread dovrebbe essere interrotto solo dal suo proprietario. Qui nell'esempio sopra chi è il proprietario? Penso che sia il thread del metodo principale.
In questo caso sei il proprietario (il proprietario è il codice che crea il thread) del CorrectPrimeProducerthread, quindi decidi cosa significa interruzione per esso. Ad esempio, potresti ricrearlo se è stato interrotto (questo accade ad esempio per Threadi pool di thread java per impostazione predefinita).