Architecture logicielle - Série distribuée à programmation simultanée Verrouillage du verrouillage et limitation des outils

 Bien que la programmation multithread améliore considérablement l'efficacité, elle comporte également certains dangers cachés. Par exemple, si deux threads insèrent des données uniques dans une table de base de données en même temps, cela peut entraîner l'insertion des mêmes données dans la base de données. Aujourd'hui, nous discuterons ensemble des problèmes de sécurité des threads et des mécanismes fournis en Java pour résoudre les problèmes de sécurité des threads. Code source: https://github.com/limingios/netFuture/blob/master/JSR133 version chinoise.pdf

Architecture logicielle - Série distribuée à programmation simultanée Verrouillage du verrouillage et limitation des outils

synchronisé et volatil

JSR133 http://www.cs.umd.edu/~pugh/java/memoryModel
FIFO (First Input First Output) signifie simplement premier entré, premier sorti.

  • Qu'est-ce qu'un verrou d'objet

    Le verrouillage d'objet, également appelé verrou de méthode, est destiné à une instance d'objet. Il déclare uniquement dans un certain emplacement de mémoire de l'objet pour identifier si l'objet a un verrou. Il ne verrouille donc que l'objet actuel et non les autres objets. Le verrou de l'instance aura un impact quelconque, et différents objets ne seront pas bloqués lors de l'accès à la même méthode modifiée par synchronized.

  • Qu'est-ce qu'un trivial

    Le verrou de classe consiste à verrouiller toute la classe. Lorsqu'il y a plusieurs threads pour déclarer des objets de cette classe, il sera bloqué jusqu'à ce que l'objet avec ce verrou de classe soit détruit ou que le verrou de classe soit activement libéré. ​​À ce stade, le thread bloqué est sélectionné. Créez un objet qui détient le verrou de la classe et déclare l'objet de la classe. D'autres threads continuent d'être bloqués.

(Baidu ci-dessus), c'est-à-dire en une phrase, peu importe le nombre d'objets, le nombre d'objets, en partagent plus d'un, et il n'y en a qu'un, peu importe comment vous l'appelez, il sera synchronisé

synchronisé

Avant de comprendre l'utilisation du mot-clé synchronized, examinons d'abord un concept: verrou d'exclusion mutuelle, comme son nom l'indique: un verrou qui peut atteindre l'objectif de l'accès d'exclusion mutuelle.

1. Pour donner un exemple simple: si vous ajoutez un verrou mutex à une ressource critique, lorsqu'un thread accède à la ressource critique, les autres threads ne peuvent qu'attendre.
2. En Java, chaque objet possède une marque de verrouillage (moniteur), également appelée moniteur. Lorsque plusieurs threads accèdent à un objet en même temps, le thread ne peut y accéder que lorsqu'il acquiert le verrou de l'objet.
3. En Java, vous pouvez utiliser le mot clé synchronized pour marquer une méthode ou un bloc de code. Lorsqu'un thread appelle la méthode synchronisée de l'objet ou accède au bloc de code synchronisé, ce thread obtient le verrou de l'objet et les autres threads ne peuvent pas temporairement Pour accéder à cette méthode, en attendant uniquement l'exécution de cette méthode ou l'exécution du bloc de code, ce thread libérera le verrou de l'objet, et d'autres threads pourront exécuter cette méthode ou ce bloc de code.

 Cependant, il y a quelques points à noter:

  1) Lorsqu'un thread accède à la méthode synchronisée d'un objet, les autres threads ne peuvent pas accéder aux autres méthodes synchronisées de l'objet. La raison en est simple, car un objet n'a qu'un seul verrou. Une fois qu'un thread a acquis le verrou de l'objet, les autres threads ne peuvent pas acquérir le verrou de l'objet et ne peuvent donc pas accéder aux autres méthodes synchronisées de l'objet.

  2) Lorsqu'un thread accède à la méthode synchronisée d'un objet, d'autres threads peuvent accéder à la méthode non synchronisée de l'objet. La raison en est très simple. L'accès à une méthode non synchronisée n'a pas besoin d'acquérir le verrou de l'objet. Si une méthode n'est pas modifiée avec le mot-clé synchronized, cela signifie qu'elle n'utilisera pas de ressources critiques, donc d'autres threads peut accéder à cette méthode.

  3) Si un thread A a besoin d'accéder à la méthode synchronisée fun1 de object1 et qu'un autre thread B a besoin d'accéder à la méthode synchronisée fun1 de object2, même si object1 et object2 sont du même type), les problèmes de sécurité des threads ne se poseront pas car ils sont accéder à différents objets, il n'y a donc pas de problème d'exclusion mutuelle.

Pour la méthode synchronisée ou le bloc de code synchronisé, lorsqu'une exception se produit, la JVM libère automatiquement le verrou occupé par le thread actuel, donc aucun blocage ne se produit en raison de l'exception

volatil

Une fois qu'une variable partagée (variable de membre de classe, variable de membre statique de classe) est modifiée par volatile, elle a deux couches de sémantique:

  1) La visibilité de cette variable est garantie lorsque différents threads fonctionnent, c'est-à-dire que si un thread modifie la valeur d'une variable, la nouvelle valeur est immédiatement visible par les autres threads.

  2) La réorganisation des instructions est interdite.

Le mot clé synchronized empêche plusieurs threads d'exécuter un morceau de code en même temps, ce qui affectera considérablement l'efficacité de l'exécution du programme. Le mot clé volatile a de meilleures performances que synchronized dans certains cas, mais il convient de noter que le mot clé volatile ne peut pas remplacer le mot-clé synchronized., Parce que le mot-clé volatile ne peut pas garantir l'atomicité de l'opération. De manière générale, l'utilisation de volatile doit répondre aux deux conditions suivantes:

  1) L'opération d'écriture dans la variable ne dépend pas de la valeur actuelle

  2) La variable n'est pas incluse dans l'invariant avec d'autres variables

  En fait, ces conditions indiquent que les valeurs effectives qui peuvent être écrites dans des variables volatiles sont indépendantes de l'état de tout programme, y compris l'état actuel de la variable.

ReentrantLock

Avant la version JDK5.0, les performances du verrou réentrant étaient bien meilleures que celles du mot-clé synchronized. Après la version JDK6.0, la synchronisation a été beaucoup optimisée et les performances des deux ne sont pas égales, mais le verrou réentrant peut remplacez complètement le mot clé. En outre, le verrou réentrant est également livré avec une série d'UBFF hautement forcés: réponse interruptible, limite de temps d'attente de l'application de verrouillage, verrouillage équitable. De plus, il peut être utilisé en combinaison avec Condition pour le rendre encore plus complet.

  • Serrure équitable

    Le verrou équitable acquiert le verrou du premier nœud de la file d'attente de synchronisation à chaque fois, et le verrou équitable acquiert le verrou à chaque fois en tant que premier nœud de la file d'attente de synchronisation, garantissant l'ordre absolu au moment de la demande de ressources. Afin de garantir l'ordre absolu du temps, les verrous équitables nécessitent des changements de contexte fréquents, tandis que les verrous non équitables réduiront certains changements de contexte et réduiront la surcharge de performances. Par conséquent, ReentrantLock sélectionne les verrous injustes par défaut pour réduire certains changements de contexte et assurer un plus grand débit du système.

  • Verrouillage injuste

    Les verrous injustes ne le sont pas nécessairement.Il est possible que le thread qui vient de libérer le verrou puisse le reprendre. Un verrou équitable peut amener le thread qui vient de libérer le verrou à continuer d'acquérir le verrou la prochaine fois, ce qui peut empêcher les autres threads d'acquérir le verrou, ce qui entraîne un phénomène de «famine».

Fournit une file d'attente basée sur FIFO qui peut être utilisée pour créer une structure de base pour les verrous ou d'autres périphériques de synchronisation associés. Le synchroniseur (appelé ci-après le synchroniseur) utilise un int pour représenter l'état, et on s'attend à ce qu'il puisse devenir la base pour atteindre la plupart des exigences de synchronisation. La méthode utilisée est l'héritage. La sous-classe gère son état en héritant du synchroniseur et doit implémenter sa méthode. La méthode de gestion consiste à manipuler l'état par des méthodes similaires à l'acquisition et à la libération. Cependant, la manipulation de l'état dans un environnement multithread doit garantir l'atomicité, les sous-classes doivent donc utiliser les trois méthodes suivantes fournies par ce synchroniseur pour fonctionner sur l'état afin de saisir l'état:

java.util.concurrent.locks.AbstractQueuedSynchronizer.getState()
java.util.concurrent.locks.AbstractQueuedSynchronizer.setState(int)
java.util.concurrent.locks.AbstractQueuedSynchronizer.compareAndSetState(int, int)

Il est recommandé de définir la sous-classe comme une classe interne d'un périphérique de synchronisation personnalisé. Le synchroniseur lui-même n'implémente aucune interface de synchronisation, il définit simplement un certain nombre d'acquisitions et d'autres méthodes d'utilisation. Le synchroniseur peut être utilisé comme mode exclusif ou mode partagé. Lorsqu'il est défini comme mode exclusif, les autres threads ne peuvent pas l'acquérir et le mode partagé peut réussir pour plusieurs threads.

Le synchroniseur est la clé de la réalisation de la serrure, le synchroniseur sert à réaliser la sémantique de la serrure puis le synchroniseur est agrégé dans la réalisation de la serrure. Cela peut être compris comme ceci: l'API de verrouillage est orientée utilisateur et définit le comportement public d'interaction avec le verrou, et chaque verrou doit effectuer une opération spécifique via ces comportements (par exemple: deux threads peuvent être autorisés à ajouter Lock, à l'exclusion de plus de deux threads), mais l'implémentation est effectuée en s'appuyant sur le synchroniseur; le synchroniseur est pour l'accès aux threads et le contrôle des ressources, qui définit si le thread peut obtenir des ressources et la mise en file d'attente des threads et d'autres opérations. Les verrous et les synchroniseurs sont une bonne séparation des zones auxquelles les deux doivent prêter attention. À proprement parler, les synchroniseurs peuvent être appliqués à d'autres fonctions de synchronisation (y compris les verrous) en plus des verrous.

AbstractQueuedSynchronizer est également appelé synchroniseur de files d'attente (ci-après appelé AQS). Il est utilisé pour créer des verrous ou d'autres composants de synchronisation. La structure de base utilise un état de variable membre de type int pour contrôler l'état de synchronisation. Lorsque state = 0, cela signifie qu'il y a est non Le thread occupe le verrou de la ressource partagée. Lorsque state = 1, cela signifie qu'un thread utilise actuellement la variable partagée et que d'autres threads doivent rejoindre la file d'attente de synchronisation pour attendre. AQS utilise en interne le nœud interne pour former un File d'attente de synchronisation FIFO pour terminer la file d'attente de verrouillage d'acquisition de thread En même temps, la classe interne ConditionObject est utilisée pour construire la file d'attente. Lorsque la condition appelle la méthode wait (), le thread rejoint la file d'attente et lorsque la condition appelle la méthode signal (), le thread passera de la file d'attente à la file d'attente de synchronisation mobile pour verrouiller la concurrence. Notez qu'il y a deux files d'attente impliquées ici, l'une est une file d'attente synchrone, lorsque le thread demande un verrou et attend, il rejoindra la file d'attente synchrone pour attendre, et l'autre est une file d'attente (il peut y en avoir plusieurs), appelez l'attente () via Condition Une fois le verrou libéré, il sera ajouté à la file d'attente.

PS: Il existe de nombreuses théories. Regardez bien les points clés. Le pdf partagé dans le code source et l'introduction de fils sur le site officiel ont été traduits en chinois par les chinois, ce qui est facile à comprendre.

Je suppose que tu aimes

Origine blog.51cto.com/12040702/2660400
conseillé
Classement