Maison >Java >javaDidacticiel >Résumé de la programmation simultanée JAVA : sécurité des threads, partage d'objets
Les threads partagent des ressources à l'échelle du processus, telles que les descripteurs de mémoire et les descripteurs de fichiers, mais chaque thread possède son propre compteur de programme (Program Counter), sa pile, ses variables locales, etc.
Plusieurs threads d'un même programme peuvent également être programmés pour s'exécuter sur plusieurs processeurs en même temps.
Le principal mécanisme de synchronisation en Java est le mot-clé synchronisé, qui fournit un méthode de verrouillage, mais le terme « synchronisation » inclut également les variables de type volatile, les verrous explicites (Explicit Lock) et les variables atomiques.
Si une synchronisation appropriée n'est pas utilisée lorsque plusieurs threads accèdent à la même variable d'état mutable, le programme générera une erreur. Il existe trois façons de résoudre ce problème :
Ne partagez pas les variables d'état entre les threads.
Modifiez la variable d'état en une variable immuable.
Utilisez la synchronisation lors de l'accès aux variables d'état.
Définition de la sécurité des threads : lorsque plusieurs threads accèdent à une classe, cette classe peut toujours afficher un comportement correct, alors cette classe est dite thread-safe .
Les objets apatrides doivent être thread-safe.
L'essence de la plupart des conditions de course : porter un jugement ou effectuer un calcul basé sur une observation potentiellement invalide. Ce type de condition de concurrence critique est appelé « vérification avant exécution » : une condition est d'abord observée comme étant vraie (par exemple, le fichier X n'existe pas), puis une action appropriée est prise en fonction de cette observation (fichier Entre le moment où vous observez ce résultat et au moment où vous commencez à créer le fichier, l'observation peut devenir invalide (un autre thread a créé des dommages au fichier, etc.).
Supposons qu'il y ait deux opérations A et B. Du point de vue du thread exécutant A, lorsqu'un autre thread exécute B, soit tous les B seront exécutés, soit B ne sera pas exécuté du tout , alors A et B sont atomiques l’un par rapport à l’autre. Une opération atomique signifie que pour toutes les opérations qui accèdent au même état (y compris l'opération elle-même), cette opération est effectuée de manière atomique.
Dans des situations pratiques, les objets thread-safe existants (tels que AtomicLong) doivent être utilisés autant que possible pour gérer l'état de la classe. Par rapport aux objets non thread-safe, il est plus facile de déterminer les états possibles des objets thread-safe et leurs transitions d'état, ce qui facilite la maintenance et la vérification de la sécurité des threads.
Pour maintenir la cohérence de l'état, toutes les variables d'état pertinentes doivent être mises à jour en une seule opération atomique.
Une façon d'implémenter la réentrance consiste à associer un nombre de get et un thread propriétaire à chaque verrou. Lorsque la valeur du compteur est 0, le verrou est considéré comme n'étant détenu par aucun thread. Lorsqu'un thread demande un verrou qui n'est pas détenu, la JVM enregistre le détenteur du verrou et définit le nombre d'acquisitions sur 1. Si le même thread acquiert à nouveau le verrou, le compteur sera incrémenté et lorsque le thread quittera le bloc synchronisé, le compteur sera décrémenté en conséquence. Lorsque le compte atteint 0, le verrou sera libéré.
Toutes les données n'ont pas besoin d'être protégées par des verrous. Seules les données variables auxquelles plusieurs threads accèdent en même temps doivent être protégées par des verrous.
Assurez-vous de ne pas maintenir les verrous lorsque vous effectuez de longs calculs ou des opérations qui peuvent ne pas se terminer rapidement (par exemple, les E/S réseau ou les E/S de la console).
, je pense que c'est une variable membre de la classe. Un objet sans état signifie que les variables membres ne peuvent pas stocker de données, ou qu'elles peuvent stocker des données mais que les données sont immuables. Les objets sans état sont thread-safe. S'il existe une variable membre dans la méthode, des opérations thread-safe pertinentes doivent être effectuées sur cette variable membre.
N'ajoutez pas aveuglément synchronisé avant la méthode. Cela peut garantir la sécurité des threads, mais la fonction de concurrence de la méthode sera affaiblie, ce qui entraînera le blocage de la méthode qui peut prendre en charge la concurrence. ce qui entraîne un ralentissement de la vitesse de traitement du programme.
Le code entouré de synchronisé doit être aussi court que possible, mais toutes les variables membres concernées doivent être conservées ensemble. Les variables membres non liées peuvent être entourées de plusieurs synchronisées.
La signification du verrouillage ne se limite pas à l'exclusion mutuelle. visibilité. Pour garantir que tous les threads voient la dernière valeur d'une variable partagée, tous les threads effectuant des opérations de lecture ou d'écriture doivent être synchronisés sur le même verrou.
Le langage Java fournit un mécanisme de synchronisation légèrement plus faible, à savoir les variables volatiles, pour garantir que les autres threads sont informés des opérations de mise à jour des variables. Lorsqu'une variable est déclarée volatile, le compilateur et le runtime remarqueront que la variable est partagée et que, par conséquent, les opérations sur la variable ne seront pas réorganisées avec d'autres opérations de mémoire. Les variables volatiles ne sont pas mises en cache dans les registres ni invisibles pour les autres processeurs, donc la lecture d'une variable de type volatile renvoie toujours la valeur écrite la plus récemment.
Aucune opération de verrouillage n'est effectuée lors de l'accès aux variables volatiles, donc le thread d'exécution ne sera pas bloqué. Par conséquent, les variables volatiles sont un mécanisme de synchronisation plus léger que le mot-clé synchronisé.
Les variables volatiles sont généralement utilisées pour marquer l'achèvement d'une opération, une interruption ou un état. La sémantique de volatile n'est pas suffisante pour garantir l'atomicité de l'opération d'incrémentation (count++) à moins que vous puissiez garantir qu'un seul thread effectue l'opération d'écriture sur la variable.
Le mécanisme de verrouillage peut assurer à la fois la visibilité et l'atomicité, tandis que les variables volatiles ne peuvent assurer que la visibilité.
Une variable volatile doit être utilisée si et seulement si toutes les conditions suivantes sont remplies :
Opérations d'écriture sur la variable ne sont pas autorisés. Dépend de la valeur actuelle de la variable, ou vous pouvez vous assurer qu'un seul thread met à jour la valeur de la variable.
Cette variable ne sera pas incluse dans la condition d'invariance avec d'autres variables d'état.
Aucun verrou n'est requis lors de l'accès à cette variable.
« Publier » un objet signifie rendre l'objet disponible pour une utilisation dans du code en dehors de la portée actuelle.
Lorsqu'un objet qui ne devrait pas être publié est publié, cette situation s'appelle une évasion.
Ne laissez pas ce plomb s'échapper pendant les travaux.
Si vous souhaitez enregistrer un écouteur d'événement ou démarrer un thread dans le constructeur, vous pouvez utiliser un constructeur privé et une méthode de fabrique publique pour éviter les erreurs dans le processus de construction.
La fermeture de pile est un cas particulier de fermeture de thread. Dans la fermeture de pile, les objets ne sont accessibles que via des variables locales.
Une manière plus standardisée de maintenir la fermeture du thread consiste à utiliser ThreadLocal. Cette classe peut associer une valeur dans le thread à l'objet qui contient la valeur.
Les objets ThreadLocal sont généralement utilisés pour empêcher le partage d'objets à instance unique mutables (Singletons) ou de variables globales.
Un objet est immuable lorsque les conditions suivantes sont remplies :
L'état d'un objet ne peut pas être modifié après sa création .
Tous les champs de l'objet sont de type final.
L'objet est créé correctement (cette référence ne s'est pas échappée lors de la création de l'objet).
Les objets immuables doivent être thread-safe.
Pour publier un objet en toute sécurité, la référence de l'objet et l'état de l'objet doivent être visibles par les autres threads en même temps. Un objet correctement construit peut être libéré en toute sécurité en :
Initialisation d'une référence d'objet dans une fonction d'initialisation statique.
Enregistrez la référence de l'objet dans un champ de type volatile ou un objet AtomicReferance.
Enregistrez la référence de l'objet dans le champ de type final d'un objet correctement construit.
Enregistre une référence à l'objet dans un champ protégé par un verrou.
Un objet immuable de facto qui est publié en toute sécurité peut être utilisé en toute sécurité par n'importe quel thread sans synchronisation supplémentaire.
Les exigences de publication d'un objet dépendent de sa mutabilité :
Les objets immuables peuvent être publiés par n'importe quel mécanisme.
Faits selon lesquels les objets immuables doivent être publiés de manière sécurisée.
Les objets mutables doivent être libérés de manière sûre et doivent être thread-safe ou protégés par un verrou.
Il existe quelques stratégies pratiques que vous pouvez utiliser lors de l'utilisation et du partage d'objets dans des programmes simultanés, notamment :
Threads fermé. Un objet inclus dans un thread ne peut appartenir qu'à un seul thread, l'objet est inclus dans ce thread et ne peut être modifié que par ce thread.
Partage en lecture seule. Sans synchronisation supplémentaire, un objet partagé en lecture seule est accessible simultanément par plusieurs threads, mais aucun thread ne peut le modifier. Les objets partagés en lecture seule incluent les objets immuables et les objets immuables de facto.
Partage thread-safe. Les objets thread-safe sont synchronisés en interne, de sorte que plusieurs threads peuvent y accéder via l'interface publique de l'objet sans autre synchronisation.
Objets protégés. Les objets protégés ne sont accessibles qu'en détenant un verrou spécifique. Les objets protégés incluent les objets encapsulés dans d'autres objets thread-safe, ainsi que les objets libérés et protégés par un verrou spécifique.
Compréhension de la libération et de l'échappement : c'est-à-dire que les variables membres ou les objets d'une classe peuvent être référencés et utilisés par d'autres classes. est libéré, comme une variable statique modifiée avec static ou l'objet de la méthode appelante actuelle. L'échappement fait référence au problème selon lequel la variable membre ou l'objet est exposé et référencé alors qu'il ne doit pas être référencé par plusieurs threads, ce qui entraîne une modification incorrecte de sa valeur. En un mot, n’étendez pas la portée d’une classe et les variables et méthodes membres utilisées en interne. C’est également une question que l’emballage doit prendre en compte.
Cet échappement : c'est-à-dire qu'un autre thread est démarré dans la classe interne du constructeur pour référencer cet objet, mais à ce stade, l'objet n'a pas encore été construit, ce qui peut conduire à erreur inattendue. La solution consiste à créer une méthode de fabrique, puis à rendre le constructeur privé.
La variable membre modifiée par final doit être initialisée dans le constructeur, sinon la variable membre ne peut pas recevoir de valeur après l'instanciation de l'objet. Lorsque la variable membre modifiée par final fait référence à un objet, l'adresse de l'objet ne peut pas être modifiée, mais la valeur de l'objet peut être modifiée.
Compréhension des quatre façons de publier un objet en toute sécurité, par exemple, il y a une référence à la classe B dans la classe A :
Méthode d'initialisation statique de A, telle que public static A a = new A(b); Dans une classe d'usine statique comme celle-ci, B est initialisé lorsque B est référencé.
La variable membre B de la classe A est modifiée avec volatile b ou AtomicReferance b.
La variable membre B dans la classe A est modifiée comme ceci avec la finale B b.
Lorsque la méthode de la classe A utilise B, elle est entourée de synchronisé(lock){B...}.
La simple compréhension des objets immuables est qu'ils sont techniquement mutables, mais ils ne seront pas modifiés pendant le traitement de la logique métier.
Recommandations associées :
Ressources partagées et à l'échelle du thread Java
Sécurité et immuabilité des threads Java
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!