Maison  >  Article  >  Java  >  Comment éviter les blocages en Java

Comment éviter les blocages en Java

怪我咯
怪我咯original
2017-07-02 10:16:321619parcourir

Les impasses peuvent être évitées dans certains cas. Cet article présentera trois techniques pour éviter les blocages. Les amis qui souhaitent savoir comment éviter les blocages en Java devraient tirer les leçons de cet article

Dans certains cas, les blocages peuvent être évités. Cet article présentera trois techniques pour éviter les blocages :

1. Séquence de verrouillage

2. Limite de temps de verrouillage

3. Détection de blocage

. Ordre de verrouillage

Lorsque plusieurs threads ont besoin des mêmes verrous mais les verrouillent dans des ordres différents, un blocage peut facilement se produire.

Si vous pouvez vous assurer que tous les threads obtiennent les verrous dans le même ordre, aucun blocage ne se produira. Regardez l'exemple suivant :

Thread 1:
 lock A 
 lock B
Thread 2:
  wait for A
  lock C (when A locked)
Thread 3:
  wait for A
  wait for B
  wait for C

Si un thread (tel que le thread 3) a besoin de verrous, alors il doit acquérir les verrous dans un certain ordre. Il ne peut acquérir les verrous suivants qu'après avoir acquis le verrou devant lui en séquence.

Par exemple, le thread 2 et le thread 3 ne peuvent essayer d'acquérir le verrou C qu'après avoir acquis le verrou A (l'acquisition du verrou A est une condition nécessaire pour acquérir le verrou C). Étant donné que le thread 1 possède déjà le verrou A, les threads 2 et 3 doivent attendre que le verrou A soit libéré. Ensuite, ils doivent réussir à verrouiller A avant d’essayer de verrouiller B ou C.

Le verrouillage dans l'ordre est un mécanisme efficace de prévention des blocages. Cependant, cette méthode nécessite de connaître à l’avance tous les verrous susceptibles d’être utilisés (et de bien séquencer ces verrous), mais elle est parfois imprévisible.

Délai de verrouillage

Une autre façon d'éviter un blocage est d'ajouter un délai d'attente lorsque vous essayez d'acquérir le verrou. si ce délai est dépassé lors du processus de tentative d'acquisition du verrou, le thread abandonnera la demande de verrouillage. Si un thread ne parvient pas à acquérir tous les verrous requis dans un délai donné, il annulera et libérera tous les verrous acquis, puis attendra une période de temps aléatoire avant de réessayer. Ce temps d'attente aléatoire donne aux autres threads la possibilité d'essayer d'acquérir les mêmes verrous, et permet à l'application de continuer à s'exécuter sans acquérir le verrou (vous pouvez continuer à s'exécuter et faire autre chose après le délai d'attente du verrouillage), puis revenez en arrière et répétez la logique de verrouillage précédente).
Ce qui suit est un exemple qui montre deux threads essayant d'acquérir les deux mêmes verrous dans des ordres différents, annulant et réessayant après un délai d'attente :

 Thread 1 locks A
Thread 2 locks B
Thread 1 attempts to lock B but is blocked
Thread 2 attempts to lock A but is blocked
Thread 1's lock attempt on B times out
Thread 1 backs up and releases A as well
Thread 1 waits randomly (e.g. 257 millis) before retrying.
Thread 2's lock attempt on A times out
Thread 2 backs up and releases B as well
Thread 2 waits randomly (e.g. 43 millis) before retrying.


Dans l'exemple ci-dessus, le thread 2 réessaye le verrou 200 millisecondes plus tôt que le thread 1, afin qu'il puisse d'abord acquérir avec succès deux verrous. A ce moment, le thread 1 tente d'acquérir le verrou A et est dans l'état d'attente . Lorsque le thread 2 se termine, le thread 1 peut également acquérir avec succès ces deux verrous (à moins que le thread 2 ou d'autres threads n'acquièrent certains des verrous avant que le thread 1 n'acquière avec succès les deux verrous).

Il est à noter qu'en raison du timeout de verrouillage, on ne peut pas penser que ce scénario doit être une impasse. Il se peut également que le thread qui a acquis le verrou (ce qui entraîne l'expiration du délai d'attente des autres threads) met beaucoup de temps à terminer sa tâche.

De plus, s'il y a plusieurs threads en compétition pour le même lot de ressources en même temps, même s'il existe un mécanisme de délai d'attente et de restauration, ces threads peuvent toujours essayer à plusieurs reprises mais ne jamais obtenir le verrou. S'il n'y a que deux threads et que le délai de nouvelle tentative est compris entre 0 et 500 millisecondes, ce phénomène peut ne pas se produire, mais s'il y a 10 ou 20 threads, la situation est différente. Parce que la probabilité que ces threads attendent des temps de nouvelle tentative égaux est beaucoup plus élevée (ou si proche que cela posera des problèmes). (Le mécanisme de délai d'attente et de nouvelle tentative vise à éviter la concurrence qui se produit en même temps. Cependant, lorsqu'il y a de nombreux threads, le délai d'expiration de deux ou plusieurs threads est très probablement le même ou proche, donc même si une concurrence se produit et qu'un un délai d'attente se produit, plus tard, parce que le délai d'attente est le même, ils commenceront à réessayer en même temps, ce qui entraînera un nouveau cycle de compétition et apportera de nouveaux problèmes)

Il y a un problème avec ce mécanisme , qui ne peut pas être utilisé en Java. Définissez le délai d'expiration pour les blocs synchronisés. Vous devez créer un verrou personnalisé ou utiliser les outils du package java.util.concurrent dans Java 5. L'écriture d'une classe de verrouillage personnalisée n'est pas compliquée, mais dépasse le cadre de cet article.

Détection des blocages

La détection des blocages est un meilleur mécanisme de prévention des blocages, qui est principalement destiné aux cas où il est impossible d'obtenir des scénarios séquentiels où le verrouillage et le délai d'expiration du verrouillage ne sont pas réalisables.

Chaque fois qu'un thread acquiert un verrou, celui-ci sera enregistré dans les structures de données liées au thread et au verrou (carte, graphique, etc.). De plus, chaque fois qu'un thread demande un verrou, celui-ci doit également être enregistré dans cette structure de données.

Lorsqu'un thread ne parvient pas à demander un verrou, le thread peut parcourir le graphique de verrouillage pour voir si un blocage se produit. Par exemple, le thread A demande le verrou 7, mais le verrou 7 est actuellement détenu par le thread B. À ce stade, le thread A peut vérifier si le thread B a demandé le verrou actuellement détenu par le thread A. Si le thread B a une telle demande, alors un blocage s'est produit (le thread A possède le verrou 1 et demande le verrou 7 ; le thread B possède le verrou 7 et demande le verrou 1).

Bien sûr, le blocage est généralement plus compliqué que la situation dans laquelle deux threads se tiennent mutuellement. Le thread A attend le thread B, le thread B attend le thread C, le thread C attend le thread D et le thread D attend le thread A. Pour que le thread A détecte un blocage, il doit détecter progressivement tous les verrous demandés par B. À partir du verrou demandé par le thread B, le thread A a trouvé le thread C, puis a trouvé le thread D et a constaté que le verrou demandé par le thread D était détenu par le thread A lui-même. C’est à ce moment-là qu’il sait qu’une impasse s’est produite.
Alors, que doivent faire ces threads lorsqu'un blocage est détecté ?

Une approche possible consiste à libérer tous les verrous, à revenir en arrière et à attendre un temps aléatoire avant de réessayer. Ceci est similaire à un simple délai d'expiration de verrouillage. La différence est que la restauration se produit uniquement lorsqu'un blocage s'est produit, et non pas parce que la demande de verrouillage a expiré. Bien qu'il y ait des délais et des attentes, s'il y a un grand nombre de threads en compétition pour le même lot de verrous, ils se bloqueront toujours à plusieurs reprises.

Une meilleure solution consiste à définir une priorité pour ces threads, à laisser un (ou plusieurs) threads revenir en arrière et les threads restants continuent comme s'ils n'avaient pas besoin de verrous. Si les priorités attribuées à ces threads sont fixes, les mêmes threads auront toujours une priorité plus élevée. Pour éviter ce problème, des priorités aléatoires peuvent être définies lorsqu'un blocage se produit.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn