Pour apprendre la programmation de concurrence Java, nous devons en apprendre davantage sur le package java.util.concurrent. Il existe de nombreuses classes d'outils de concurrence que nous utilisons souvent dans ce package, telles que: ReentrantLock, CountdownLatch, Cyclicbarrier, Semaphore, etc. L'implémentation sous-jacente de ces classes reposait toutes sur la classe AbstractqueeEdSynchronizer, qui montre l'importance de cette classe. Donc, dans la série de concurrenances Java, j'ai d'abord analysé la classe AbstractqueuedSynchronizer. Étant donné que cette classe est plus importante et que le code est relativement long, afin de l'analyser aussi complètement que possible, j'ai décidé d'utiliser quatre articles pour donner une introduction relativement complète à cette classe. Cet article est une introduction par résumé pour donner aux lecteurs une compréhension préliminaire de cette catégorie. Par souci de simplicité de narration, certains endroits utiliseront AQS pour représenter cette classe à l'avenir.
1. Quelle est la classe AbstractqueueEdSynchronizer?
Je crois que de nombreux lecteurs ont utilisé Reentrantlock, mais ils ne connaissent pas l'existence de l'abstraitqueEuedynchronizer. En fait, ReentrantLock met en œuvre une synchronisation de classe interne, qui hérite de l'abstractqueEuedSynchronizer. Toutes les implémentations du mécanisme de verrouillage reposent sur des classes internes de synchronisation. On peut également dire que la mise en œuvre de Reentrantlock dépend de la classe AbstractqueEuedynchronizer. De même, les classes CountdownLatch, CyclicBarrier et Semaphore utilisent également la même méthode pour implémenter leur propre contrôle des verrous. On peut voir que l'abstractqueEuedynchronizer est la pierre angulaire de ces classes. Alors, qu'est-ce qui est implémenté exactement dans AQS afin que toutes ces classes en dépendent? On peut dire que AQS fournit une infrastructure pour ces classes, c'est-à-dire qu'il fournit un verrouillage de mot de passe. Une fois que ces classes ont un verrouillage de mot de passe, ils peuvent définir le mot de passe du verrouillage du mot de passe par eux-mêmes. De plus, AQS fournit également une zone de file d'attente et un instructeur de fil. Nous savons que les fils sont comme un barbare primitif. Ils ne savent pas comment être polis. Ils ne feront que se précipiter, donc vous devez l'enseigner étape par étape, lui dire quand il doit faire la queue, où faire la queue, quoi faire avant la file d'attente et quoi faire après la file d'attente. Tous ces travaux éducatifs sont achevés par AQS pour vous. Les fils qui y sont éduqués sont devenus très civilisés et polis, et ne sont plus des barbares primitifs. Ainsi, à l'avenir, nous n'avons qu'à traiter ces fils civilisés. Je n'ai jamais trop de contact avec les fils d'origine!
2. Pourquoi AbstractQueEuedSynchronizer propose-t-il un verrouillage de mot de passe?
// le nœud de tête de la file d'attente de synchronisation Tête de nœud volatile transitoire privé; // Le nœud de queue de la file d'attente de synchronisation Tourne de nœuds volatils transitoires privés; // l'état int privé volatile; // obtenir l'état de synchronisation Final int GetState () {return State;} // Set Synchronisation State Protected Void SetState (int NewState) {State = NewState;} // Set Synchronisation State State Protected Boolan compared compared (INTTATED) Synchronisation State State Protected Boolan compared comparean compareans (INTTERNE) {return usare.compareAndSwapint (ceci, StateOffset, attendez, mise à jour);}Le code ci-dessus répertorie toutes les variables membres d'AQS. Vous pouvez voir qu'il n'y a que trois variables membre d'AQ, à savoir la référence du nœud de tête de file d'attente de synchronisation, la référence du nœud de queue de file d'attente de synchronisation et l'état de synchronisation. Notez que les trois variables membre sont modifiées avec le mot clé volatil, qui garantit que plusieurs threads le modifient sont visibles à la mémoire. Le noyau de toute la classe est cet état de synchronisation. Vous pouvez voir que l'état de synchronisation est en fait une variable de type int. Vous pouvez considérer cet état de synchronisation comme un verrouillage de mot de passe, et il s'agit également d'un verrouillage de mot de passe verrouillé de la pièce. La valeur spécifique de l'état est équivalente au mot de passe contrôlant l'ouverture et la fermeture du verrouillage du mot de passe. Bien sûr, le mot de passe de ce verrou est déterminé par chaque sous-classe. Par exemple, dans Reentrantlock, l'état est égal à 0 signifie que le verrou est ouvert, l'état supérieur à 0 signifie que le verrou est verrouillé et en sémaphore, l'état supérieur à 0 signifie que le verrou est ouvert et que l'état est égal à 0 signifie que le verrou est verrouillé.
3. Comment la zone de file d'attente de l'abstractqueuedSynchronizer est-elle mise en œuvre?
Il y a en fait deux zones de file d'attente à l'intérieur de l'abstractqueEuedSynchronizer, l'une est une file d'attente synchrone et l'autre est une file d'attente conditionnelle. Comme on peut le voir sur la figure ci-dessus, il n'y a qu'une seule file d'attente de synchronisation, alors qu'il peut y avoir plusieurs files d'attente de conditions. Les nœuds de la file d'attente synchrone conservent des références aux nœuds avant et arrière respectivement, tandis que les nœuds de la file d'attente conditionnelle n'ont qu'une seule référence au nœud successeur. Dans la figure, T représente un fil. Chaque nœud contient un fil. Une fois que le thread ne parvient pas à acquérir le verrou, il entre d'abord dans la file d'attente de synchronisation dans la file d'attente. Si vous souhaitez saisir la file d'attente conditionnelle, le fil doit maintenir le verrou. Ensuite, jetons un coup d'œil à la structure de chaque nœud dans la file d'attente.
// Les nœuds de la file d'attente synchrone sont le nœud de classe finale statique {le nœud final statique partagé = new node (); // Le thread actuel maintient le verrou en mode partagé nœud final statique exclusif = null; // Le thread actuel maintient le verrou en mode exclusif statique final Int annulé = 1; // Le nœud actuel a annulé le Signal Final Int statique de verrouillage = -1; // Les threads du nœud successeur doivent exécuter la condition intatique finale finale = -2; // Le nœud actuel est en file d'attente dans la file d'attente conditionnelle statique final int propagate = -3; // Le nœud suivant peut acquérir directement le verrouillage volatil INT Waitstatus; // indique l'état d'attente du nœud de nœud actuel Précédent; // indique le nœud avant dans le nœud volatile de la file d'attente de synchronisation ensuite; // indique le nœud successeur dans le thread volatil de file d'attente de file d'attente de synchronisation; // Le thread maintenu par le nœud actuel fait référence au nœud Nextwaitit; // indique le nœud successeur dans la file d'attente conditionnelle // est l'état de nœud actuel dans le mode partagé final booléen iSShared () {return nextwait == partagé; } // Renvoie le nœud avant du nœud actuel Final Node Predecesseur () lève NullPointerException {Node P = PREV; if (p == null) {lancer un nouveau nullpointerException (); } else {return p; }} // Constructeur 1 Node () {} // Constructeur 2, ce constructeur est utilisé par le nœud par défaut (Thread Thread, Node Mode) {// Notez que le mode de maintien est attribué à NextWaitter this.nextWaitter = mode; this.thread = thread; } // Constructeur 3, seul le nœud (thread thread, int waitstatus) est utilisé dans la file d'attente de condition {this.waitstatus = waitstatus; this.thread = thread; }}Le nœud représente un nœud dans la file d'attente de synchronisation et la file d'attente conditionnelle. Il s'agit d'une classe intérieure d'AbstractqueEuedynchronizer. Le nœud a de nombreux attributs, tels que le mode de maintien, l'état d'attente, la pré-séquence et le successeur dans les files d'attente synchrones, et les références successives dans les files d'attente conditionnelles, etc. La file d'attente de synchronisation et la file d'attente de condition peut être considérée comme une zone de file d'attente, chaque nœud est considéré comme un siège dans la zone de file d'attente, et le fil est considéré comme une file d'attente. Lorsque les invités arrivent pour la première fois, ils frapperont à la porte pour voir si la serrure est ouverte. Si la serrure n'est pas ouverte, ils iront dans la zone de file d'attente pour collecter une plaque numérique, déclarer de quelle manière ils veulent maintenir la serrure, et enfin faire la queue à la fin de la file d'attente.
4 Comment comprendre le mode exclusif et le mode de partage?
Comme mentionné précédemment, chaque invité recevra une plaque numérique avant la file d'attente et déclarera qu'il veut posséder la serrure. La façon de posséder le verrou est divisé en mode exclusif et mode de partage. Alors, comment comprenez-vous le mode exclusif et le mode de partage? Je ne trouve vraiment aucune bonne analogie. Vous pouvez penser à des toilettes publiques. Les personnes à mode exclusive sont plus dominantes. Je n'entre pas. Les gens en mode de partage ne sont pas si particuliers. Quand ils constatent que les toilettes sont déjà utilisables, elle ne compte pas si elle entre elle-même. Ils doivent également demander avec enthousiasme les gens derrière eux. Si les gens derrière ne les dérangent pas de l'utiliser ensemble, il n'est pas nécessaire de faire la queue. Tout le monde ira ensemble. Bien sûr, si les gens derrière eux l'esprit, ils doivent rester dans la file d'attente et continuer à faire la queue.
5 Comment comprendre l'état d'attente d'un nœud?
Nous voyons également que chaque nœud a un état d'attente, qui est divisé en quatre états: annulé, signal, condition et propage. Cet état d'attente peut être considéré comme un panneau suspendu à côté du siège, identifiant l'état d'attente de la personne sur le siège actuel. Le statut de cette marque peut non seulement être modifié par vous-même, mais d'autres peuvent également le modifier. Par exemple, lorsque ce fil a déjà prévu d'abandonner pendant la file d'attente, il définira le panneau sur son siège annulé, afin que d'autres puissent le dégager de la file d'attente s'ils le voient. Une autre situation est que lorsque le fil est sur le point de s'endormir sur le siège, il a peur qu'il ne dorme trop, donc cela changera le signe de la position avant pour signaler, car tout le monde reviendra sur ses sièges avant de quitter la file d'attente pour jeter un coup d'œil. S'il voit que le statut sur le signe est le signal, il réveillera la prochaine personne. Ce n'est qu'en veillant à ce que la marque en position avant soit signal, le fil actuel dormira paisiblement. L'état de la condition indique que le thread est en file d'attente dans la file d'attente conditionnelle. L'état de propagation rappelle les threads suivants pour acquérir directement le verrou. Ce statut n'est utilisé que dans le mode partagé et sera discuté plus tard lorsque vous parlez séparément du mode partagé.
6. Quelles opérations seront effectuées lorsqu'un nœud entre dans la file d'attente de synchronisation?
// Fonctionnement de l'enquille du nœud, retournez au nœud privé du nœud précédent enq (nœud de nœud final) {pour (;;) {// Obtenez la référence au nœud de queue du nœud de file d'attente de synchronisation T = queue; // Si le nœud de queue est vide, cela signifie que la file d'attente de synchronisation n'a pas été initialisée if (t == null) {// initialiser la file d'attente de synchronisation if (comparabledEthEad (new node ())) {tail = tête; }} else {// 1. Pointer vers le nœud de queue actuel node.prev = t; // 2. Définissez le nœud actuel sur le nœud de queue if (CompareAndSettail (t, nœud)) {// 3. Pointer le successeur de l'ancien nœud de queue vers le nouveau nœud de queue t.next = nœud; // La seule sortie de la boucle pour les rendements t; }}}}Notez que l'opération d'Enqueue utilise une boucle morte. Ce n'est que lorsque le nœud est ajouté avec succès à la queue de la file d'attente de synchronisation sera retourné. Le résultat est le nœud de queue d'origine de la file d'attente de synchronisation. La figure suivante montre l'intégralité du processus de fonctionnement.
Les lecteurs doivent faire attention à l'ordre d'ajouter des nœuds de queue, qui sont divisés en trois étapes: pointant des nœuds de queue, CAS modifie les nœuds de queue et pointant les successeurs de l'ancien nœud de queue vers le nœud actuel. Dans un environnement simultané, ces trois étapes peuvent ne pas être garanties. Par conséquent, dans le fonctionnement de la compensation de tous les nœuds annulés dans la file d'attente de synchronisation, afin de trouver des nœuds dans un état non canalaire, il n'est pas traversé de l'avant à dos mais de l'arrière à l'avant. De plus, lorsque chaque nœud entre dans la file d'attente, son état d'attente est 0. Ce n'est que lorsque le thread du nœud suivant doit être suspendu, l'état d'attente du nœud précédent sera modifié en signal.
Remarque: Toute l'analyse ci-dessus est basée sur JDK1.7, et il y aura des différences entre différentes versions, les lecteurs doivent faire attention.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.