Concurrence Java - Stratégies d'interruption
Je lis Java Concurrency en pratique . Dans la section Politiques d'interruption du chapitre
Annulation et arrêt
Son mentionné
Une tâche ne doit rien supposer de la politique d'interruption de son thread d'exécution, sauf si elle est explicitement conçue pour s'exécuter dans un service qui a une politique d'interruption spécifique. Qu'une tâche interprète l'interruption comme une annulation ou effectue une autre action en cas d'interruption, elle doit prendre soin de conserver l'état d'interruption du thread en cours d'exécution. S'il ne propage pas InterruptedException à son appelant, il doit restaurer l'état d'interruption après avoir intercepté InterruptionException: Thread.currentThread (). Interrupt ()
J'ai donc essayé de jouer avec un échantillon de liste pour comprendre. Mais je suis confus avec la sortie.
PremierProducteur
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éthode 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;
}
Production:
// 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
Question
Simplement en ajoutant TimeUnit.SECONDS.sleep (5) dans le thread principal de la classe principale. L'état d'interruption du thread en cours d'exécution (c'est-à-dire du générateur) est en cours de réinitialisation. Si je commente la méthode TimeUnit.SECONDS.sleep (5), dans ce cas, l'état d'interruption est conservé. Pourquoi cela se produit-il et comment?
Dans le livre, il est mentionné Un fil ne doit être interrompu que par son propriétaire. Ici, dans l'exemple ci-dessus, qui est le propriétaire? Je pense que son fil de méthode principal.
Réponses
En ajoutant, TimeUnit.SECONDS.sleep(5)vous donnez suffisamment de temps au thread pour se terminer.
Lorsqu'un thread se termine, son indicateur d'interruption est effacé.
Ce n'est pas documenté dans la spécification, mais c'est ce qui se passe. Voir par exemple ce rapport de bogue :
Il n'y a pas de spécification violée ici, donc j'ai fait une demande d'amélioration plutôt qu'un bogue. On peut soutenir que le manque de spécification est un bogue - nous avons intentionnellement spécifié que "l'interruption après la fin n'a pas d'effet" pour traiter le fait que l'état d'interruption est stocké dans la VM et n'existe plus une fois qu'un thread s'est terminé. Cependant, nous avons négligé de refléter cela dans la spécification Thread.isInterrupted.
Sans le supplément sleep, je soupçonne qu'en théorie, vous pourriez voir les deux trueet falseinterrompre le statut car il y a une condition de concurrence, mais il est beaucoup plus probable que vous le verrez truegrâce à la planification des threads. La fenêtre de temps où l'état de l'interruption est faux, entre la levée de l'exception et la restauration de l'état de l'interruption dans le bloc catch, est incroyablement petite.
Simplement en ajoutant TimeUnit.SECONDS.sleep (5) dans le thread principal de la classe principale. L'état d'interruption du thread en cours d'exécution (c'est-à-dire du générateur) est en cours de réinitialisation. Si je commente la méthode TimeUnit.SECONDS.sleep (5), dans ce cas, l'état d'interruption est conservé. Pourquoi cela se produit-il et comment?
Vous n'utilisez aucun mécanisme de synchronisation (à part le blocage de la file d'attente) entre le thread principal et CorrectPrimeProducerainsi de suite lorsque le thread principal imprime l'état - il se CorrectPrimeProducerpeut qu'il n'ait pas encore conservé l'état d'interruption (en exécutant catchdes instructions de blocage), vous obtenez falseainsi le résultat.
Lorsque vous ajoutez sleepau principal, Threadvous augmentez simplement la possibilité que le CorrectPrimeProducerthread préserve l'état d'interruption en invoquant catchdes instructions de blocage avant que le thread principal n'essaie d'imprimer son état. C'est pourquoi il imprime true.
Dans le livre, il est mentionné Un fil ne doit être interrompu que par son propriétaire. Ici, dans l'exemple ci-dessus, qui est le propriétaire? Je pense que son fil de méthode principal.
Dans ce cas, vous êtes le propriétaire (le propriétaire est le code qui crée le thread) du CorrectPrimeProducerthread, vous décidez donc de ce que l'interruption signifie pour lui. Par exemple, vous pouvez le recréer s'il a été interrompu (cela se produit par exemple pour les Threads de pools de threads java par défaut).