Préface
Pour InterruptedException, une façon courante de le gérer est de "l'avaler" - attraper et ne rien faire (ou l'enregistrer, mais ce n'est pas beaucoup mieux) - tout comme la liste 4 plus tard. Malheureusement, cette approche ignore le fait que les interruptions peuvent se produire au cours de cette période, et les interruptions peuvent faire perdre la capacité de l'application à annuler l'activité ou à la fermer dans le temps.
Méthode de blocage
Lorsqu'une méthode lance une interruption, il vous indique non seulement qu'elle peut lancer une exception de contrôle spécifique, mais elle vous indique également autre chose. Par exemple, il vous indique qu'il s'agit d'une méthode de blocage, qui essaiera d'éliminer le blocage et le retour le plus tôt possible si vous répondez correctement.
La méthode de blocage est différente de la méthode générale qui prend beaucoup de temps à exécuter. L'achèvement d'une méthode générale dépend uniquement de ce qu'il fait et s'il y a suffisamment de ressources informatiques disponibles (cycles de CPU et mémoire). L'achèvement de la méthode de blocage dépend également de certains événements externes, tels que l'expiration du minuteur, l'achèvement d'E / S ou les actions d'un autre thread (relâchez un verrou, définissez un drapeau ou placez une tâche dans une file d'attente de travail). Les méthodes générales se terminent une fois leur travail terminé, tandis que les méthodes de blocage sont plus difficiles à prévoir car elles dépendent d'événements externes. Les méthodes de blocage peuvent affecter la réactivité car il est difficile de prédire quand ils se termineront.
La méthode de blocage peut ne pas être terminée car elle ne peut pas attendre l'événement qu'il attend, il est donc très utile de rendre la méthode de blocage annulée (et elle est souvent très utile si les méthodes non bloquantes de longue date sont annulées). Une opération annule fait référence à une opération qui peut être résiliée de l'extérieur avant l'achèvement normal. Le mécanisme d'interruption fourni par Thread et pris en charge par Thread.Sleep () et Object.Wait () est un mécanisme d'annulation; Il permet à un thread de demander un autre fil pour arrêter ce qu'il fait. Lorsqu'une méthode lance une interruption, elle vous dit que si le thread exécutant la méthode est interrompu, il essaiera d'arrêter ce qu'il fait et de revenir à l'avance, et en lançant une conception interrompue, il revient à l'avance. Une méthode de bibliothèque de blocage bien comportée devrait répondre aux interruptions et lancer une conception interrompue afin qu'elle puisse être utilisée dans les activités annuables sans affecter la réponse.
Interruption du fil
Chaque thread a un attribut booléen qui lui est associé, qui représente l'état interrompu du thread. L'état d'interruption est faux au début; Lorsqu'un autre thread interrompt un thread en appelant thread.interrupt (), l'une des deux situations se produit. Si ce thread exécute une méthode de blocage interruptible de bas niveau, tel que Thread.Sleep (), Thread.Join () ou Object.Wait (), il débloquera et lancera une interruption. Sinon, Interrupt () définit simplement l'état d'interruption du fil. Une fois que le code exécuté dans le thread interrompu peut interroger l'état d'interruption pour voir s'il est demandé d'arrêter ce qu'il fait. L'état d'interruption peut être lu par thread.isinterrupted () et peut être lu et effacé par une opération appelée thread.interrupted ().
L'interruption est un mécanisme collaboratif. Lorsqu'un fil interrompt un autre fil, le fil interrompu n'arrête pas nécessairement ce qu'il fait immédiatement. Au lieu de cela, une interruption est une demande polie à un autre fil pour arrêter ce qu'elle fait lorsqu'elle est disposée et pratique. Certaines méthodes, telles que Thread.Sleep (), prennent ces demandes très au sérieux, mais chaque méthode ne répond pas nécessairement aux interruptions. Pour les demandes d'interruption, les méthodes qui ne bloquent pas mais prennent toujours beaucoup de temps à exécuter peuvent interroger l'état d'interruption et revenir à l'avance lorsqu'ils sont interrompus. Vous pouvez ignorer les demandes d'interruption à volonté, mais cela affectera la réponse.
Un avantage de la nature collaborative des interruptions est qu'elle offre une plus grande flexibilité pour construire en toute sécurité les activités annuables. Nous voulons rarement qu'une activité s'arrête immédiatement; Si l'activité est annulée pendant une mise à jour en cours, la structure des données du programme peut être incohérente. L'interruption permet à une activité annule de nettoyer les travaux en cours, de restaurer les invariants, d'informer d'autres activités qu'il doit être annulé avant sa résiliation.
Manipuler InterruptedException
Si lancer une interruption de l'interruption signifie qu'une méthode est une méthode de blocage, appeler une méthode de blocage signifie que votre méthode est également une méthode de blocage, et vous devriez avoir une sorte de stratégie pour gérer l'interruption de l'Exception. La stratégie la plus simple consiste généralement à lancer vous-même une interruption, comme le montre le code dans les méthodes puttask () et getTask () dans la liste 1. Cela fait que la méthode répond aux interruptions et ajoute simplement une conception interrompue à la clause des lancers.
Listing 1. Ne pas attraper InterruptedException, le propager à l'appelant
classe publique TaskQueue {private static final int max_tasks = 1000; BlockingQueue privée <Task> Queue = new LinkedBlockerQueue <Task> (Max_Tasks); public void puttask (tâche r) lève InterruptedException {queue.put (r); } tâche publique getTask () lance InterruptedException {return queue.take (); }} Parfois, certains travaux de nettoyage sont nécessaires avant que l'exception ne se propage. Dans ce cas, vous pouvez attraper l'interruptedException, effectuer un nettoyage, puis lancer une exception. Listing 2 démontre cette technique, un mécanisme utilisé pour faire correspondre les joueurs dans les services de jeu en ligne. La méthode MatchPlayers () attend que deux joueurs arrivent, puis commencent un nouveau jeu. Si la méthode est interrompue lorsqu'un joueur est arrivé mais qu'un autre joueur n'est pas arrivé, il remettra ce joueur dans la file d'attente et remonte l'interruption de l'interruption afin que la demande du joueur au jeu ne soit pas perdue.
Listing 2. Effectuer des travaux de nettoyage spécifiques à la tâche avant de remédier à InterruptedException
classe publique PlayerMatcher {joueurs de joueurs privés; Public PlayerMatcher (joueurs de joueurs) {this.players = joueurs; } public void MatchPlayers () lève InterruptedException {try {Player PlayerOne, playertwo; while (true) {playeone = playertwo = null; // attendez que deux joueurs arrivent et démarrent un nouveau jeu PlayerOne = Players.WaitForplayer (); // pourrait lancer IE playertwo = player.WaitForplayer (); // pourrait lancer IE startNewgame (playerone, playertwo); }} catch (InterruptedException e) {// Si nous obtenions un joueur et que nous avons été interrompus, remettez ce lecteur si (playerOne! = null) Players.AddFirst (playerOne); // propage ensuite le jet d'exception e; }}} N'arrêtez pas d'avaler vivant
Parfois, lancer une interruption de l'interruption n'est pas approprié, par exemple, lorsqu'une tâche définie par un runnable appelle une méthode interruptible. Dans ce cas, l'interruptedException ne peut pas être remontée, mais vous ne voulez rien faire non plus. Lorsqu'une méthode de blocage détecte une interruption et lance une interruption, elle efface l'état d'interruption. Si une exception interrompue est capturée mais ne peut pas être remontée, les preuves de l'interruption se produisent doivent être conservées afin que le code de niveau supérieur dans la pile d'appels puisse connaître l'interruption et y répondre. Cette tâche peut être effectuée en appelant Interrupt () pour "réinterrompre" le thread actuel, comme indiqué dans Listing 3. Au moins, chaque fois qu'une interruption est capturée et qu'elle n'est pas rénovée, le thread actuel est réinterrompu avant de revenir.
Listing 3. Reprendre l'état interrompu après avoir capturé InterruptedException
classe publique TaskRunner implémente Runnable {private BlockerQueue <Task> file d'attente; Public TaskRunner (BlockingQueue <Task> Queue) {this.queue = file d'attente; } public void run () {try {while (true) {tâche task = queue.take (10, timeunit.seconds); tâche.execute (); }} catch (InterruptedException e) {// Restaurer le thread d'état interrompu.currentThread (). Interrupt (); }}} La pire chose à faire lors de la gestion de l'interruption, c'est de l'avaler brute - l'attraper, puis de le remédier ni de réaffirmer l'état d'interruption du fil. Pour une exception que vous ne savez pas comment gérer, la façon la plus standard de gérer est de l'attraper et de l'enregistrer, mais cette méthode est toujours comme une interruption brute, car le code de niveau supérieur dans la pile d'appels ne peut toujours pas obtenir d'informations sur l'exception. (Il n'est pas sage d'enregistrer simplement InterruptedException, car il est trop tard pour le traiter lorsque quelqu'un vient lire le journal.) Listing 4 montre un modèle largement utilisé, qui est également un modèle d'interruption de déglutition brute:
Liste 4. Interruption de déglutition brute - ne faites pas cela
// Ne faites pas cette classe publique TaskRunner implémente Runnable {Private BlockingQueue <Task> file d'attente; Public TaskRunner (BlockingQueue <Task> Queue) {this.queue = file d'attente; } public void run () {try {while (true) {tâche task = queue.take (10, timeunit.seconds); tâche.execute (); }} catch (interruptedException avalé) {/ * Ne faites pas cela - restaurez le statut interrompu à la place * /}}} Si l'interruptedException ne peut pas être remonté, que vous prévoyez de traiter ou non la demande d'interruption, vous devez toujours réintégraliser le thread actuel, car une demande d'interruption peut avoir plusieurs "récepteurs". La mise en œuvre du thread de filetage standard (ThreadPoolExecutor) est responsable de l'interruption, donc l'interruption d'une tâche dans un pool de threads en cours d'exécution peut avoir un double effet. L'un consiste à annuler la tâche, et l'autre consiste à informer le thread d'exécution que le pool de threads est sur le point d'être fermé. Si la tâche dévore la demande, le fil du travailleur ne saura pas qu'il existe une interruption demandée, en retardant la fermeture de l'application ou du service.
Implémentation des tâches annulées
Il n'y a pas de sémantique spécifique pour les interruptions de la spécification linguistique, mais dans les programmes plus importants, il est difficile de maintenir une sémantique d'interruption, sauf l'annulation. Selon l'activité, les utilisateurs peuvent demander l'annulation via une interface graphique ou via un mécanisme de réseau, tel que JMX ou service Web. La logique du programme peut également être priée d'annuler. Par exemple, un robot Web Web s'arrête automatiquement s'il détecte que le disque est plein, sinon un algorithme parallèle commencera plusieurs threads pour rechercher différentes zones de l'espace de la solution, et annuler ces threads une fois que l'un des threads trouvera une solution.
Ce n'est pas parce qu'une tâche est annulée qu'une demande d'interruption doit être répondue immédiatement. Pour les tâches qui exécutent du code dans une boucle, il est généralement nécessaire de vérifier les interruptions une fois pour chaque itération de boucle. Selon la durée de l'exécution de la boucle, il peut prendre un certain temps à n'importe quel code pour remarquer que le thread a été interrompu (soit interroger l'état d'interruption en appelant la méthode thread.isterrupted (), soit appeler une méthode de blocage). Si la tâche doit être réactive, elle peut interroger l'état d'interruption plus fréquemment. La méthode de blocage interroge généralement l'état d'interruption immédiatement à l'entrée et, si elle est définie pour améliorer la réactivité, il lance également une exception interrompue.
La seule fois où vous pouvez arrêter la déglutition vivante, c'est que vous savez que le fil est sur le point de quitter. Ce scénario ne se produit que lorsque la classe qui appelle la méthode interruptible fait partie du thread, pas de code de bibliothèque exécutable ou général, comme le montre la liste 5. Listing 5 crée un thread qui répertorie les nombres premiers jusqu'à ce qu'il soit interrompu, ce qui est également autorisé à quitter lorsqu'il est interrompu. La boucle utilisée pour rechercher des primes vérifie les interruptions à deux endroits: l'une consiste à interroger la méthode Isinterrupted () à la tête de la boucle while, et l'autre est d'appeler la méthode de blocage BlockingQueue.put ().
Listing 5. Si vous savez que le fil est sur le point de quitter, vous pouvez avaler l'interruption
La classe publique PrimeProducer étend Thread {Final BlockingQueue Final <BigInteger> file d'attente; PrimeProducer (BlockingQueue <BigInteger> file d'attente) {this.queue = file d'attente; } public void run () {try {bigInteger p = bigInteger.one; while (! thread.currentThread (). isInterrupted ()) queue.put (p = p.NextProbableprime ()); } catch (InterruptedException consommé) {/ * Autoriser le thread à quitter * /}} public void annule () {interrupt (); }} Méthode de blocage sans interruption
Toutes les méthodes de blocage ne lancent pas l'interruption. Les classes de flux d'entrée et de sortie en attente de l'E / S se terminent, mais elles ne lancent pas InterruptedException et ne reviendront pas à l'avance si elles sont interrompues. Cependant, pour les E / S de socket, si un fil ferme la prise, l'opération d'E / S de blocage sur cette prise se terminera tôt et une socketexception est lancée. Les classes d'E / S non bloquantes dans java.nio ne prennent pas en charge les E / S interruptibles, mais ils peuvent également être débloqués en fermant le canal ou en demandant un réveil sur le sélecteur. De même, tenter d'acquérir un verrou interne (entrant dans un bloc synchronisé) ne peut pas être interrompu, mais ReentrantLoc prend en charge le mode d'acquisition interruptible.
Tâches non scellables
Certaines tâches refusent d'être interrompues, ce qui les rend sans couverture. Cependant, même les tâches non licenciables devraient essayer de préserver l'état d'interruption au cas où le code de niveau supérieur sur la pile d'appels doit traiter les interruptions une fois les tâches non ciblables terminées. Listing 6 montre une méthode qui attend une file d'attente de blocage jusqu'à ce qu'un élément disponible apparaisse dans la file d'attente, qu'il soit interrompu ou non. Pour plus de commodité, il reprend l'état d'interruption après la fin dans un bloc enfin, afin de ne pas priver l'appelant de la demande d'interruption. (Il ne peut pas reprendre l'état d'interruption plus tôt, car cela provoquera une boucle infinie - BlockingQueue.Take () interrogera immédiatement l'état d'interruption à l'entrée et, si l'état d'interruption est trouvé, une interruption de l'interruption sera lancée.)
Listing 6. Tâches non scellables qui restaurent l'état interrompu avant de revenir
Tâche publique getNextTask (BlockingQueue <Task> file d'attente) {Boolean Interrupted = false; try {while (true) {try {return queue.take (); } catch (InterruptedException e) {interrupted = true; // tombe et réessayer}}} enfin {if (interrompu) thread.currentThread (). Interrupt (); }} Résumer
Vous pouvez utiliser le mécanisme d'interruption de collaboration fourni par la plate-forme Java pour construire des stratégies d'annulation flexibles. Les activités peuvent décider à leur pouvoir discrétionnaire s'ils sont annuables ou non imprimés, et comment répondre aux interruptions, et peuvent également reporter les interruptions si le retour immédiat compromet l'intégrité de l'application. Même si vous voulez ignorer complètement les interruptions dans votre code, vous devez vous assurer que l'état d'interruption est restauré sans le reconstituer, afin que le code l'appelle ne sait pas ce qui se passe l'interruption. Ce qui précède est le contenu complet de la théorie et de la pratique de Java de gérer les exceptions d'interruption de l'Exception. J'espère que cet article vous sera utile. Si vous avez des questions, veuillez laisser un message à la discussion.