Le modèle de mémoire Java (JMM) définit comment les threads interagissent avec la mémoire, régissant la visibilité, l'ordre et l'atomicité des mises à jour variables sur les threads. 2. Sans une bonne synchronisation, un fil peut ne pas voir les modifications d'autrui en raison de la mise en cache ou de la réorganisation de l'instruction. 3. La relation survenue avant assure la visibilité et l'ordre, établies par ordre de programme, verrous, variables volatiles, démarrage / jointure du fil. 4. Déclarer un volatile variable garantit la visibilité et empêche la réorganisation, ce qui le rend adapté aux drapeaux mais pas aux opérations de composés atomiques. 5. Les opérations atomiques comme l'incrément nécessitent un INTOMICInteger, des blocs synchronisés ou des verrous pour éviter les conditions de course. 6. Les blocs synchronisés fournissent une exclusion mutuelle et des garanties en provenance, garantissant une publication s?re des données partagées. 7. Les champs finaux dans des objets immuables correctement construits sont garantis pour être visibles par d'autres threads sans synchronisation supplémentaire. 8. Les meilleures pratiques incluent l'utilisation du volatile pour les drapeaux simples, le tirage des utilitaires Java.util.Concurrent, la préférence de l'immuabilité et la dépendance à la commande intuitive. 9. Testez toujours le code simultané dans des conditions multipliées réelles, car les conditions de course peuvent ne pas appara?tre dans l'exécution unique. 10. Comprendre le JMM permet d'écrire des programmes simultanés corrects et prévisibles par conception plut?t que par hasard.
Lorsque vous travaillez avec Java, en particulier dans les applications simultanées ou hautes performances, la compréhension du modèle de mémoire Java (JMM) est cruciale. Il définit comment les threads interagissent à travers la mémoire et quels comportements sont légaux dans le code multithread. Alors que Java résume une grande partie de la gestion de la mémoire via la collecte des ordures, le JMM régit la visibilité, la commande et l'atomicité des opérations de la mémoire à travers les fils - et le faire se tromper peut entra?ner des problèmes subtils et difficiles à déborder comme les conditions de course, les données stales ou les boucles infinies.

Décomposons le modèle de mémoire Java en termes pratiques.
Quel est le modèle de mémoire Java?
Le modèle de mémoire Java est une spécification au sein de la spécification du langage Java (JLS) qui décrit comment les threads interagissent avec la mémoire dans une application Java. Il ne dicte pas comment la mémoire est disposée (comme le tas ou la pile), mais définit plut?t les règles pour les modifications des variables apportées par un seul fil doit devenir visible pour les autres .

Idée clé: sans synchronisation appropriée, un thread peut ne pas voir les mises à jour effectuées par un autre thread - même si ces mises à jour se sont produites il y a longtemps.
Cela se produit parce que:

- Chaque thread peut mettre en cache des variables dans les registres du processeur ou le cache local.
- Le compilateur et le processeur peuvent réorganiser les instructions pour l'optimisation (tant que la sémantique unique est conservée).
- Le JMM établit des limites sur ces comportements pour permettre à la fois les performances et la concurrence prévisible.
Le problème: visibilité et réorganisation
Imaginez ce scénario:
// Variables partagées int value = 0; Boolean Ready = false; // Fil 1 valeur = 42; Ready = true; // Fil 2 tandis que (! prêt) { // rotation } System.out.println (valeur); // Qu'est-ce qui est imprimé?
Vous pourriez vous attendre à ce que cela imprime 42
. Mais sans synchronisation , le JMM permet:
- Fil 2 pour ne jamais quitter la boucle (en raison de la mise en cache
ready
). - Thread 2 pour voir
ready == true
maisvalue == 0
(si les écritures sont réorganisées ou non rincées). - Le compilateur pour réorganiser
value = 42
etready = true
dans le thread 1.
C'est là que le JMM intervient - en définissant des relations.
Se produit avant: la garantie de base
La relation en passant avant est la pierre angulaire du JMM. Si une action se produit avant une autre, la première est visible et ordonnée avant la seconde.
Quelques moyens clés pour établir des arrivants avant:
- Règle de l'ordre du programme : chaque thread a une relation provenant avant les actions dans l'ordre dans lequel ils apparaissent dans le code.
- Règle de verrouillage du moniteur : un déverrouillage sur un moniteur se produit avant chaque verrouillage suivant sur ce même moniteur.
- Règle de variable volatile : une écriture à une variable volatile se produit avant chaque lecture ultérieure de cette même variable volatile.
- Règle de démarrage du thread : appelant
thread.start()
arrive avant toutes les actions du thread démarré. - Règle de jointure de thread : toutes les actions dans un thread se produisent avant le retour de
join()
.
Retour à notre exemple - si nous rendons ready
à être volatils :
Boolean volatile prêt = false;
Maintenant:
- L'écriture à
value
se produit avant l'écriture àready
(ordre du programme). - L'écriture à
ready
se produit avant la lecture deready
dans Thread 2 (règle volatile). - Par conséquent, la lecture de
value
dans le thread 2 voit l'écriture du thread 1.
Résultat: garanti pour imprimer 42
.
Volatile: plus que "pas de mise en cache"
De nombreux développeurs pensent que volatile
empêche la mise en cache - mais il fait deux choses:
- Assure la visibilité : les écritures sont immédiatement victimes de la mémoire principale, les lectures proviennent de la mémoire principale.
- Empêche la réorganisation : le JVM et le CPU ne réorganiseront pas les lectures / écrit autour d'un accès volatil.
Spécifiquement:
- Les lectures d'une variable volatile ne peuvent pas être réorganisées avec une lecture / écriture précédente.
- Les écritures d'une variable volatile ne peuvent pas être réorganisées avec une lecture / écriture ultérieure.
Cela rend volatile
utile pour les drapeaux et les indicateurs d'état - mais pas suffisant pour les opérations composées comme count
.
Atomicité et conditions de course
Le JMM définit également quelles opérations sont atomiques.
Atomique garanti:
- Lire / écriture aux types
int
etreference
. - Lit / écrit à un double
volatile long
etvolatile double
(car ils sont traités spécialement).
Pas atomique:
- Des lectures / écritures régulières à
long
etdouble
(peuvent être divisées en deux opérations 32 bits - bien que sur la plupart des JVM modernes, ils sont effectivement atomiques). - Des actions composées comme
i
(Read-Modify-Write), même surint
.
Ainsi, même si une variable est visible, vous pouvez toujours avoir des conditions de course:
volatile int compter = 0; // dans plusieurs threads: comptoir ; // pas atomique! Besoin de synchronisation.
Utilisez AtomicInteger
, synchronized
ou lock
pour de tels cas.
Synchronisation et le JMM
Les blocs synchronized
font plus qu'une exclusion mutuelle - ils établissent des relations.
Lorsqu'un thread quitte un bloc synchronized
:
- Toutes les écritures avant la libération du moniteur se produisent avant le prochain thread qui l'acquiert.
Exemple:
Objet Lock = new Object (); int data = 0; Boolean Ready = false; // Fil 1 synchronisé (verrouillage) { données = 42; Ready = true; } // Fil 2 synchronisé (verrouillage) { if (ready) { System.out.println (données); // garanti de voir 42 } }
Même sans volatile
, les blocs synchronisés créent une cha?ne se produit avant, garantissant la visibilité.
Champs finaux et le JMM
Une partie souvent négligée du JMM est la fa?on dont il traite les champs final
.
Point clé: Les champs finaux correctement construits sont toujours visibles par d'autres threads sans synchronisation .
Exemple:
classe publique ImmutableObject { Valeur int finale; nom de cha?ne final; public ImmutableObject (Int Value, String Name) { this.value = valeur; this.name = name; } }
Si un thread publie en toute sécurité une instance d' ImmutableObject
(par exemple, via une liste correctement construite ou un champ statique), d'autres threads verront les valeurs correctes de value
et name
- même si l'objet est partagé sans synchronisation.
Mais cela ne fonctionne que si:
- La référence à l'objet ne s'échappe pas pendant la construction.
- L'objet est effectivement immuable ou vraiment immuable.
Plats à emporter
Pour écrire le code Java concurrent correct:
- Utilisez volatile pour des drapeaux simples ou des variables d'état.
- Utilisez des classes synchronisées , reentrantlock ou atomiques pour les opérations composées.
- Préférez les objets immuables dans la mesure du possible - ils sont en file d'assistance par conception.
- Comprenez que les variables locales sont en filetage (chaque thread a sa propre pile), mais les objets partagés ne le sont pas.
- évitez de s'appuyer sur la commande ?intuitive? - utilisez des règles en provenance pour raisonner sur l'exactitude.
Remarque finale: ne lancez pas votre propre synchronisation
Le JMM est complexe et la sémantique à mémoire de bas niveau est facile à se tromper. Dans la plupart des cas:
- Utilisez des utilitaires de concurrence de niveau supérieur de
java.util.concurrent
:ConcurrentHashMap
,AtomicInteger
,BlockingQueue
, etc. - Conception de l'immuabilité et de l'apatridité.
- Tester sous une concurrence réelle - les conditions de course n'apparaissent souvent pas dans les tests à thread unique.
Comprendre le JMM n'éliminera pas les bogues, mais il vous aide à écrire du code qui est correct par conception - pas par chance.
Fondamentalement, c'est la base invisible qui rend la concurrence de Java à la fois puissante et périlleuse.
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!

Outils d'IA chauds

Undress AI Tool
Images de déshabillage gratuites

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
échangez les visages dans n'importe quelle vidéo sans effort grace à notre outil d'échange de visage AI entièrement gratuit?!

Article chaud

Outils chauds

Bloc-notes++7.3.1
éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds

Les énumérations en Java sont des classes spéciales qui représentent le nombre fixe de valeurs constantes. 1. Utilisez la définition du mot-clé énuméré; 2. Chaque valeur d'énumération est une instance finale statique publique du type d'énumération; 3. Il peut inclure des champs, des constructeurs et des méthodes pour ajouter un comportement à chaque constante; 4. Il peut être utilisé dans les instructions de commutation, prend en charge la comparaison directe et fournit des méthodes intégrées telles que Name (), Ordinal (), Values ??() et ValueOf (); 5. L'énumération peut améliorer la sécurité, la lisibilité et la flexibilité du type, et convient aux scénarios de collecte limités tels que les codes d'état, les couleurs ou la semaine.

Le principe d'isolement de l'interface (ISP) exige que les clients ne comptent pas sur des interfaces inutilisées. Le noyau est de remplacer les interfaces grandes et complètes par plusieurs interfaces petites et raffinées. Les violations de ce principe comprennent: une exception non implémentée a été lancée lorsque la classe met en ?uvre une interface, un grand nombre de méthodes non valides sont implémentées et des fonctions non pertinentes sont classées de force dans la même interface. Les méthodes d'application incluent: Diviser les interfaces en fonction des méthodes communes, en utilisant des interfaces divisées en fonction des clients et en utilisant des combinaisons au lieu d'implémentations multi-interfaces si nécessaire. Par exemple, divisez les interfaces machine contenant des méthodes d'impression, de balayage et de fax en imprimante, scanner et faxmachine. Les règles peuvent être assouplies de manière appropriée lors de l'utilisation de toutes les méthodes sur de petits projets ou tous les clients.

Java prend en charge la programmation asynchrone, y compris l'utilisation de la transition complète, des flux réactifs (tels que ProjectActor) et des threads virtuels dans Java19. 1.COMPLETABLEFUTURE Améliore la lisibilité et la maintenance du code à travers les appels de cha?ne et prend en charge l'orchestration des taches et la gestion des exceptions; 2. ProjectAacteur fournit des types de mono et de flux pour implémenter une programmation réactive, avec mécanisme de contre-pression et des opérateurs riches; 3. Les fils virtuels réduisent les co?ts de concurrence, conviennent aux taches à forte intensité d'E / S et sont plus légères et plus faciles à développer que les fils de plate-forme traditionnels. Chaque méthode a des scénarios applicables, et les outils appropriés doivent être sélectionnés en fonction de vos besoins et les modèles mixtes doivent être évités pour maintenir la simplicité

Il existe trois principales différences entre lesquelles appelant et coulable en Java. Tout d'abord, la méthode callable peut renvoyer le résultat, adapté aux taches qui doivent retourner des valeurs, telles que callable; Alors que la méthode Run () de Runnable n'a pas de valeur de retour, adaptée aux taches qui n'ont pas besoin de retourner, comme la journalisation. Deuxièmement, Callable permet de lancer des exceptions vérifiées pour faciliter la transmission d'erreur; tandis que Runnable doit gérer les exceptions en interne. Troisièmement, Runnable peut être directement transmis sur le thread ou l'exécutor-service, tandis que Callable ne peut être soumis qu'à ExecutorService et renvoie le futur objet à

En Java, les énumérations conviennent à représenter des ensembles constants fixes. Les meilleures pratiques incluent: 1. Utilisez ENUM pour représenter l'état fixe ou les options pour améliorer la sécurité et la lisibilité des types; 2. Ajouter des propriétés et des méthodes aux énumérations pour améliorer la flexibilité, telles que la définition des champs, des constructeurs, des méthodes d'assistance, etc.; 3. Utilisez Enuummap et Enumset pour améliorer les performances et la sécurité des types car ils sont plus efficaces en fonction des tableaux; 4. évitez l'abus des énumérations, tels que des valeurs dynamiques, des changements fréquents ou des scénarios logiques complexes, qui doivent être remplacés par d'autres méthodes. L'utilisation correcte de l'énumération peut améliorer la qualité du code et réduire les erreurs, mais vous devez faire attention à ses limites applicables.

Javanio est un nouvel IOAPI introduit par Java 1.4. 1) s'adresse aux tampons et aux canaux, 2) contient des composants de tampon, de canal et de sélecteur, 3) prend en charge le mode non bloquant et 4) gère les connexions simultanées plus efficacement que l'OI traditionnel. Ses avantages se reflètent dans: 1) IO non bloquant les réductions de la surcharge du thread, 2) le tampon améliore l'efficacité de transmission des données, 3) le sélecteur réalise le multiplexage et 4) la cartographie de la mémoire accélère la lecture et l'écriture de la lecture de fichiers. Remarque Lorsque vous utilisez: 1) le fonctionnement FLIP / clair du tampon est facile à confondre, 2) les données incomplètes doivent être traitées manuellement sans blocage, 3) l'enregistrement du sélecteur doit être annulé à temps, 4) Nio ne convient pas à tous les scénarios.

JavaprovidesMultiplesynchronisationToolsforthReadsafety.1.SynchroniséBlockSenSureMutualExclusionByLockingMethodSorseCificcodesesections.2.ReentrantLockoffersAdvancedControl, y compris les éperons

Le mécanisme de chargement des classes de Java est implémenté via Classloader, et son flux de travail principal est divisé en trois étapes: chargement, liaison et initialisation. Pendant la phase de chargement, Classloader lit dynamiquement le bytecode de la classe et crée des objets de classe; Les liens incluent la vérification de l'exactitude de la classe, l'allocation de la mémoire aux variables statiques et les références de symbole d'analyse; L'initialisation effectue des blocs de code statique et des affectations de variables statiques. Le chargement des classes adopte le modèle de délégation parent et hiérarchise le chargeur de classe parent pour trouver des classes et essayez Bootstrap, Extension et ApplicationClassloader pour s'assurer que la bibliothèque de classe de base est s?re et évite le chargement en double. Les développeurs peuvent personnaliser le chargeur de classe, comme UrlClassl
