Depuis Java 5, le package java.util.concurrent.locks contient des implémentations de verrouillage, vous n'avez donc plus à implémenter vos propres serrures. Mais vous devez toujours comprendre comment utiliser ces serrures.
Un simple verrouillage
Commençons par un bloc de synchronisation à Java:
classe publique Counter {private int count = 0; public int inc () {synchronisé (this) {return ++ count; }}}Vous pouvez voir qu'il existe un bloc de code synchronisé (ce) dans la méthode Inc (). Ce bloc de code peut garantir qu'un seul thread peut exécuter le nombre de retour ++ en même temps. Bien que le code du bloc de synchronisation synchronisé puisse être plus complexe, le fonctionnement simple du nombre de ++ est suffisant pour exprimer la signification de la synchronisation du thread.
La classe de comptoir suivante utilise le verrouillage au lieu de synchronisé pour atteindre le même objectif:
classe publique Counter {Private Lock Lock = new Lock (); INT privé Count = 0; public int inc () {lock.lock (); int newCount = ++ Count; lock.unlock (); retourner newCount; }}La méthode Lock () verrouille l'objet d'instance de verrouillage, de sorte que tous les threads qui appellent la méthode Lock () sur l'objet seront bloqués jusqu'à ce que la méthode déverrouillée () de l'objet de verrouillage soit appelée.
Voici une simple implémentation de la classe Lock:
classe publique Counter {public class Lock {private boolean islocked = false; public synchronisé void Lock () lève InterruptedException {while (islocked) {wait (); } islocked = true; } public synchronisé void unlock () {islocked = false; notify (); }}Notez la boucle while (islocked) à l'intérieur, qui est également appelée "verrouillage de spin". Lorsque le blocage est vrai, le fil d'appel de fil Lock () bloque et attend sur l'appel d'attente (). Pour empêcher le thread de revenir de attendre () sans recevoir l'appel Notify () (également appelé faux réveil), le thread revérira la condition islocked pour déterminer s'il peut être continué en toute sécurité à s'exécuter ou doit continuer à attendre, au lieu de penser que le fil peut être continué à s'exécuter en toute sécurité après avoir été réveillé. Si IsClocked est faux, le thread actuel quittera la boucle While (islocked) et remets est enraciné sur true, de sorte que d'autres threads appelant la méthode Lock () peuvent ajouter des verrous sur l'instance de verrouillage.
Lorsque le thread termine le code dans la section critique (situé entre Lock () et Unlock ()), unlock () est appelé. L'exécution de Unlock () sera réinstallé à Reclocked en false et notive (réveiller) l'un des threads qui ont appelé la fonction attend () dans la méthode Lock () et sont dans un état d'attente.
Réentrabilité des serrures
Le bloc de synchronisation synchronisé en Java est réentrant. Cela signifie que si un thread Java entre dans le bloc de synchronisation synchronisé dans le code et obtient ainsi un verrou sur le tuyau correspondant à l'objet de synchronisation utilisé par le bloc de synchronisation, le thread peut alors entrer un autre bloc de code Java synchronisé par le même objet de pipeline. Voici un exemple:
classe publique reentrant {public synchronisé exter () {inner (); } public synchronisé inner () {// faire quelque chose}}Notez que l'extérieur () et inner () sont déclarés synchronisés, ce qui équivaut à des blocs synchronisés (ce) en Java. Si un thread appelle OUTER (), il n'y a pas de problème à appeler Inner () dans OUTER (), car les deux méthodes (blocs de code) sont synchronisées par le même objet de gestion ("ceci"). Si un thread a déjà un verrou sur un objet de tuyau, il a accès à tous les blocs de code synchronisés par l'objet de tuyau. C'est réentrant. Un thread peut saisir n'importe quel bloc de code synchronisé par un verrouillage qu'il a déjà.
L'implémentation de verrouillage donnée ci-dessus n'est pas réentrente. Si nous réécrivons la classe réentrante comme celle suivante, lorsque le thread appelle OUTER (), il bloquera sur Lock.lock () de la méthode intérieure ().
classe publique reentrant2 {Lock Lock = new Lock (); public outer () {lock.lock (); intérieur(); lock.unlock (); } public synchronisé inner () {lock.lock (); // faire quelque chose Lock.unlock (); }}Le thread appelant OUTER () verrouille d'abord l'instance de verrouillage, puis continuera à appeler Inner (). Dans la méthode Inner (), le thread essaiera de verrouiller à nouveau l'instance de verrouillage, et l'action échouera (c'est-à-dire que le fil sera bloqué), car l'instance de verrouillage est déjà verrouillée dans la méthode externe ().
Si unlock () n'est pas appelé entre Lock () deux fois, le deuxième appel à verrouiller bloquera. Après avoir vu la mise en œuvre de Lock (), vous constaterez que la raison est évidente:
Lock de classe publique {boolean islocked = false; public synchronisé void Lock () lève InterruptedException {while (islocked) {wait (); } islocked = true; } ...}La question de savoir si un fil est autorisé à quitter la méthode Lock () est déterminé par les conditions de la boucle WHOR (verrouillage de spin). La condition de jugement actuelle est que l'opération de verrouillage n'est autorisée que lorsque l'est bloqué est faux, sans considérer quel thread le verrouille.
Afin de rendre cette classe de verrouillage réentable, nous devons y faire un petit changement:
Lock de classe publique {boolean islocked = false; Thread LockedBy = NULL; int LockedCount = 0; Public synchronisé void Lock () lève InterruptedException {Thread CallThread = Thread.currentThread (); while (islocked && LockedBy! = CallThread) {wait (); } islocked = true; LockedCount ++; LockedBy = CallThread; } public synchronisé void unlock () {if (thread.currentThread () == this.lockedBy) {LockedCount--; if (LockedCount == 0) {islocked = false; notify (); }}} ...}Notez que la boucle actuelle (Lock Spin) prend également en compte le fil qui a verrouillé l'instance de verrouillage. Si l'objet de verrouillage actuel n'est pas verrouillé (islocked = false), ou si le thread d'appel actuel a verrouillé l'instance de verrouillage, la boucle while ne sera pas exécutée, et que le fil d'appel du thread () peut quitter la méthode (note du traducteur: "autorisé à quitter la méthode" dans la sémantique actuelle signifie qu'il n'appellera pas attendre () et provoquer le blocage).
De plus, nous devons enregistrer le nombre de fois que le même thread verrouille à plusieurs reprises un objet de verrouillage. Sinon, un appel Unlock () débloquera l'ensemble du verrou, même si le verrouillage actuel a été verrouillé plusieurs fois. Nous ne voulons pas que le verrouillage soit déverrouillé tant que l'appel déverrouillé () atteint le nombre de fois que l'appel de verrouillage () correspondant est appelé.
Maintenant, cette classe de verrouillage est réentrente.
L'équité de la serrure
Les blocs synchronisés de Java ne garantissent pas l'ordre dans lequel les threads tentent de les entrer. Par conséquent, si plusieurs threads continuent de rivaliser pour accéder au même bloc de synchronisation synchronisé, il existe un risque qu'un ou plusieurs threads n'auront jamais accès - c'est-à-dire que l'accès est toujours attribué à d'autres threads. Cette situation s'appelle la faim de fil. Pour éviter ce problème, les serrures doivent atteindre l'équité. Les verrous indiqués dans cet article sont mis en œuvre en interne avec des blocs de synchronisation synchronisés, ils ne sont donc pas garantis pour être justes.
Appelez unlock () dans la déclaration enfin
Si le verrouillage est utilisé pour protéger la zone critique et que la zone critique peut lancer une exception, il est très important d'appeler unlock () dans l'instruction enfin. Cela garantit que l'objet de verrouillage peut être déverrouillé afin que d'autres threads puissent continuer à le verrouiller. Voici un exemple:
lock.lock (); essayez {// faire du code de section critique, // qui peut lancer l'exception} enfin {lock.unlock ();}Cette structure simple garantit que l'objet de verrouillage peut être déverrouillé lorsqu'une exception est lancée dans la zone critique. Si unlock () n'est pas appelé dans l'instruction ENFIN, lorsqu'une exception est lancée dans la section critique, l'objet de verrouillage restera à l'état verrouillé pour toujours, ce qui fera bloquer tous les autres threads appelant Lock () sur l'objet de verrouillage.
Ce qui précède est les informations sur le verrouillage multithread Java. Nous continuerons d'ajouter des informations pertinentes à l'avenir. Merci pour votre soutien à ce site!