Dans notre travail de développement quotidien, nous sommes plus ou moins exposés à la programmation multithread ou à certains problèmes de concurrence. Avec la mise à niveau des systèmes d'exploitation et du matériel système, la programmation simultanée est de plus en plus utilisée dans notre Au cours du développement, notre idée originale de Utiliser le multi-threading consiste à utiliser davantage les ressources du système. Cependant, lorsque nous utilisons le multi-threading, il y aura également quelques problèmes. Examinons d'abord un morceau de code.
private static int i = 0; private static void increse(){ i++; } public static void main(String[] args) { Thread[] threads = new Thread[20]; for (int i = 0; i < threads.length; i++){ threads[i] = new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 10000; j++){ increse(); } } }); threads[i].start(); } while (Thread.activeCount() > 1){ Thread.yield(); } System.out.println(i); }
Tout d'abord, il n'y a aucun problème si vous regardez ce code, mais si vous exécutez ce code dans un environnement multithread, les résultats de l'exécution de ce code sont fondamentalement différents. Ici, 20. les threads sont démarrés, puis chaque thread appelle la méthode increse()
pour effectuer une opération d'affectation sur la variable i
. La sortie attendue devrait être 200000
, mais pourquoi la sortie est-elle différente à chaque fois ? La raison réside à cet endroit i++
. C'est la cause première des problèmes de concurrence. Alors pourquoi cela apparemment simple i++
a-t-il un tel problème ? Comprenons-le simplement à partir de java内存模型
(JMM). Tout d'abord, JMM
stipule que chaque thread aura une mémoire de travail lors de son exécution, puis la variable i
est stockée dans la mémoire principale à chaque fois que le thread calcule des données. le temps, vous devez accéder à la mémoire principale pour obtenir la valeur de la variable actuelle. Pour faire simple, après qu'un thread ait calculé la variable i
et obtenu le résultat, il n'a pas actualisé les données dans la mémoire principale. cette fois, d'autres threads ont obtenu la valeur d'origine. En d'autres termes, on ne sait pas si les données obtenues dans ce thread sont les plus récentes. Mais ce n'est qu'une brève explication du point de vue de JMM
. Cette opération apparemment simple de i++
contient en fait trois opérations, obtenir la valeur de i
, incrémenter i
, puis incrémenter i
Faire une affectation. . Lors de ces opérations, les autres threads auront beaucoup de temps pour faire beaucoup de choses. Alors certaines personnes pourront se demander, est-ce suffisant pour synchroniser cette i++
opération ? Oui, comment le synchroniser ?
Certaines personnes disent qu'utiliser volatile
pour modifier la variable i
est suffisant ? Oui, le mot-clé java
dans volatile
fournit bien une fonction de synchronisation, mais pourquoi le modifier ici n'a-t-il toujours aucun effet ? La raison en est que si une variable est modifiée avec volatile, cela permettra uniquement aux autres threads de connaître immédiatement la valeur de la variable actuelle, qui est appelée 可见性
ici, mais cela ne peut toujours pas résoudre les problèmes de ces opérations de i++
, et alors ? Pour résoudre ce problème, quelqu'un d'autre a proposé d'utiliser le mot-clé synchronized
. Je dois admettre que ce mot-clé est en effet très puissant et peut résoudre ce problème. Nous avons donc réfléchi à la raison pour laquelle cela peut résoudre ce problème. est résolu ? C'est toujours Pour faire simple, tout d'abord, synchronized
appartient au niveau JVM. Les méthodes ou blocs de code avec ce mot-clé seront finalement interprétés comme des instructions monitorenter
et monitorexit
. un reference
tapez des paramètres pour indiquer l'objet à verrouiller ou déverrouiller, comme ceci :
public synchronized String f(){ //code } synchronized(object){ //code }
En voyant cela, nous devrions pouvoir comprendre pourquoi synchronized
peut résoudre le problème du programme ci-dessus, mais nous devrions aussi Un concept clair est 原子性
En d'autres termes, lorsque nous traitons de certains problèmes multithread, nous devons nous assurer que certaines opérations sur les données partagées sont atomiques, afin de garantir l'exactitude, je crois. vous l'avez aussi Pour une compréhension générale, résumons les points à noter lorsqu'on traite des problèmes de multi-threading, 可见性
, 原子性
, 有序性
, ces points garantissent que le multi-threading peut être correct Une condition préalable. car ce qu'est l'ordre, cela implique la réorganisation des instructions de mémoire, ce qui n'entre pas dans le cadre de la discussion. Nous en discuterons plus tard.
Je voudrais également souligner un autre problème ici, à savoir si nous devons synchroniser ou verrouiller lorsque nous traitons de problèmes multithread. Ce n'est pas certain. Il y a eu une blague sur Internet auparavant, c'est quand. nous traitons de problèmes multithread, nous constatons parfois que le code est écrit en monothread. Bien sûr, ce n'est qu'une blague, mais ici nous pouvons aussi voir si les programmes monothread n'ont pas ces questions ? La réponse est oui, car il n’y a pas de problème de concurrence entre les ressources dans un seul thread, il n’est donc pas nécessaire d’en discuter à nouveau.
Alors, quand devons-nous utiliser la synchronisation, et quand ne le faisons-nous pas ? Jetons un coup d'œil à un morceau de code
public String f(String s1, String s2, String s3){ return s1 + s2 +s3; }
Il s'agit d'une méthode d'épissage de chaînes. Décompilons et regardons comment la JVM fait-elle ici ?
Recommandations associées :
Explication détaillée de la synchronisation des threads Java et des méthodes de synchronisation
Introduction détaillée au bloc de synchronisation Java. Utilisez
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!