Notes de lecture-Java Concurrent Programming Combat-Chapter 2 Thread Safety

La prémisse des problèmes de sécurité des threads:

Cette variable peut être modifiée

La variable est accessible par plusieurs threads

Lorsque plusieurs threads accèdent à la même variable d'état variable sans synchronisation appropriée, une erreur se produit dans le programme. Il existe trois façons de résoudre ce problème:

  • Ne partagez pas la variable d'état entre les threads
  • Modifier les variables d'état en variables immuables
  • Lors de votre visite, utilisez la synchronisation

1. Qu'est-ce que la sécurité des fils?

Sécurité des threads: lorsque plusieurs threads accèdent à une certaine classe, la classe peut se comporter correctement, alors nous appelons cette classe thread-safe.

Les objets sans état doivent être thread-safe

(Avec état et sans état: avec état signifie une classe ayant pour fonction de stocker des données. Sans état signifie que les attributs de la classe ne stockent pas de données)

2. Atomicité

1. Condition de concurrence: lorsque plusieurs threads accèdent à la même ressource, l'ordre d'exécution différent des threads entraînera des résultats incohérents. À ce stade, une condition de concurrence se produit. Les ressources de code accessibles par plusieurs threads sont appelées sections critiques.

  • Vérifiez d'abord puis exécutez: faites un jugement basé sur une observation qui peut échouer, ou effectuez une certaine opération. Ce type de condition de concurrence est appelé vérification avant exécution.

2. Conditions de course dans l' initialisation paresseuse : l' initialisation paresseuse initialisera l'objet quand il sera nécessaire pour économiser de la mémoire. En même temps, nous devons nous assurer que l'objet n'est initialisé qu'une seule fois. Cependant, dans le cas du multithreading, deux threads exécutent la condition d'initialisation en même temps. Lorsque l'initialisation du premier thread n'est pas terminée, le second thread exécutera le jugement de condition à ce moment. Le résultat est que l'objet n'a pas été initialisé, puis le deuxième thread effectuera à nouveau l'initialisation. Des problèmes de sécurité des threads sont survenus à ce moment. Le code qui aurait dû être exécuté une fois a été exécuté une fois par deux threads. S'il y a un autre thread pour juger des conditions d'initialisation avant que l'initialisation des 1er et 2e threads ne soit terminée. Ensuite, une autre initialisation sera effectuée. C'est une situation que nous ne voulons pas voir.

3. Opération composée: La combinaison de plusieurs actions qui doivent être effectuées de manière atomique est appelée opération composée. Par exemple, vérifiez d'abord, puis exécutez, il existe un ensemble d'actions à exécuter de manière atomique. Donc vérifier d'abord, puis exécuter est une opération composée. Si nous voulons garantir la sécurité des threads, nous devons nous assurer que chaque groupe d'opérations composées est exécuté de manière atomique. De manière générale, nous atteindrons cet objectif par le verrouillage.

Trois, mécanisme de verrouillage

1. Verrou intégré: Java fournit un mécanisme de verrouillage intégré pour prendre en charge l'atomicité: blocs de code synchronisés. Le bloc de code de synchronisation se compose de deux parties: l'une est la référence d'objet de la serrure, l'autre est le bloc de code de protection de la serrure. La méthode modifiée avec le mot-clé Synchronized est une méthode de synchronisation qui inclut tout le corps de la méthode. Le verrou de ce bloc de code est une instance de l'appel de cette méthode. C'est ça. Lorsque la modification synchronisée est une méthode statique, le verrou du bloc de code est l'objet .CLASS de la classe courante.

2. Réentrant: lorsqu'un thread demande un verrou détenu par un autre thread, le thread demandeur sera bloqué. Cependant, étant donné que le verrou interne est réentrant, si un thread tente d'acquérir un verrou qu'il détient déjà, la demande aboutira. Une façon d'obtenir la réentrance consiste à associer le verrou à un compteur et à un thread propriétaire. Lorsque le compteur est égal à 0, le verrou n'est détenu par aucun thread. À ce stade, tout thread qui s'applique au verrou peut réussir. En même temps, la machine virtuelle Java enregistre le détenteur du verrou et définit le compteur sur 1. Lorsque le même thread est à nouveau En acquérant ce verrou, le compteur s'incrémentera, et lorsque le thread sort du bloc de code de synchronisation, le compteur diminuera jusqu'à ce que le compteur atteigne 0 et que le verrou soit libéré.

3. Les avantages de la réentrée : lorsque la sous-classe remplace la méthode de synchronisation de la classe parente, comme indiqué dans le code suivant. Lors de l'exécution de doSomething de la sous-classe LoggingWidget, le thread obtiendra le verrou. Lorsque le doSomething de la sous-classe est exécuté, le doSomething du parent est à nouveau appelé. Si le verrou intégré n'est pas réentrant, le doSomething de la classe parent attendra indéfiniment. La réentrée évite ce genre de blocage.

public class Widget{
    
    
    public synchronized void doSomething(){
    
    
        ...
    }
}

public class LoggingWidget extends Widget{
    
    
    public synchronized void doSomething(){
    
    
        system.out.println("=================");
        super.doSomething();
    }
}

Quatrièmement, utilisez un verrou pour protéger l'État

Les variables peuvent être protégées par des verrous, ce qui garantit qu'un seul thread exécute cette variable à la fois. Ce n'est que lorsque le thread termine l'opération sur la variable que d'autres threads peuvent opérer sur la variable.

5. Activité et performance

  • Le verrouillage peut en effet assurer la sécurité de notre programme. Nous devons tenir compte de la sécurité parce que: le multi-thread est utilisé pour améliorer les performances Dans le cas du multi-thread, des problèmes de sécurité des threads se produiront, nous devons donc verrouiller pour assurer la synchronisation. Mais cela ne signifie pas que nous devons sacrifier les performances pour la sécurité. Par conséquent, nous devons assurer la sécurité du programme tout en garantissant la performance. Ne sacrifiez pas les performances pour une sécurité aveugle. Ensuite, nous introduisons le multithreading et cela n'a aucun sens.
  • Par conséquent, lorsque nous verrouillons, nous devons porter un jugement raisonnable sur la taille du bloc de code de synchronisation. À ce stade, nous devons trouver un compromis entre sécurité, simplicité et performances. Il y a souvent des conflits entre simplicité et performance.

Remarque: lorsque vous effectuez des calculs à long terme ou des opérations qui risquent de ne pas être terminées rapidement (E / S réseau ou E / S console), ne maintenez pas le verrou

Je suppose que tu aimes

Origine blog.csdn.net/weixin_45373852/article/details/108726264
conseillé
Classement