Maison  >  Article  >  Java  >  Résoudre le problème de la réorganisation des instructions Java

Résoudre le problème de la réorganisation des instructions Java

黄舟
黄舟original
2017-09-28 09:23:472012parcourir

L'éditeur suivant vous proposera une brève discussion sur la question de la réorganisation des instructions Java. L'éditeur le trouve plutôt bon, je vais donc le partager avec vous maintenant et le donner comme référence pour tout le monde. Suivons l'éditeur et jetons un œil.

La réorganisation des instructions est un problème relativement compliqué et quelque peu incroyable. Je commence aussi par un exemple (il est recommandé d'exécuter l'exemple, il peut en fait être reproduit. La probabilité. de réorganisation est encore assez élevé), j'ai une compréhension perceptuelle


/**
 * 一个简单的展示Happen-Before的例子.
 * 这里有两个共享变量:a和flag,初始值分别为0和false.在ThreadA中先给  a=1,然后flag=true.
 * 如果按照有序的话,那么在ThreadB中如果if(flag)成功的话,则应该a=1,而a=a*1之后a仍然为1,下方的if(a==0)应该永远不会为
 * 真,永远不会打印.
 * 但实际情况是:在试验100次的情况下会出现0次或几次的打印结果,而试验1000次结果更明显,有十几次打印.
 */
public class SimpleHappenBefore {
 /** 这是一个验证结果的变量 */
 private static int a=0;
 /** 这是一个标志位 */
 private static boolean flag=false;
 public static void main(String[] args) throws InterruptedException {
  //由于多线程情况下未必会试出重排序的结论,所以多试一些次
  for(int i=0;i<1000;i++){
   ThreadA threadA=new ThreadA();
   ThreadB threadB=new ThreadB();
   threadA.start();
   threadB.start();
   //这里等待线程结束后,重置共享变量,以使验证结果的工作变得简单些.
   threadA.join();
   threadB.join();
   a=0;
   flag=false;
  }
 }
 static class ThreadA extends Thread{
  public void run(){
  a=1;
  flag=true;
  }
 }
 static class ThreadB extends Thread{
  public void run(){
   if(flag){
   a=a*1;
   }
   if(a==0){
   System.out.println("ha,a==0");
   }
  }
 }
}

L'exemple est relativement simple, et des commentaires ont été ajoutés, je ne le décrirai donc pas dans détail.

Qu'est-ce que la réorganisation des instructions ? Il existe deux niveaux :

Au niveau de la machine virtuelle, afin de minimiser l'impact de la vacance du processeur causée par la vitesse de fonctionnement de la mémoire qui est beaucoup plus lente que la vitesse de fonctionnement du processeur, la machine virtuelle suivra certaines de ses propres règles (cette règle sera décrite plus tard) perturbe l'ordre dans lequel le programme est écrit - c'est-à-dire que le code écrit plus tard peut être exécuté en premier dans l'ordre chronologique, tandis que le code écrit plus tôt sera exécuté plus tard - afin d'utiliser au maximum le CPU. Prenons l'exemple ci-dessus : s'il ne s'agit pas de l'opération a=1, mais de a=new byte[1024*1024] (allouer 1 M d'espace)", alors il fonctionnera très lentement. À ce moment-là, le CPU attend son. exécution pour terminer , ou devrions-nous d'abord exécuter le flag=true suivant ? De toute évidence, l'exécution de flag=true en premier peut utiliser le processeur à l'avance et accélérer l'efficacité globale. Bien sûr, ce principe est qu'aucune erreur ne se produira (quel type d'erreur sera discuté plus tard). Bien qu'il y ait deux situations ici : le code ultérieur commence à s'exécuter avant le code précédent ; le code précédent commence à s'exécuter en premier, mais lorsque l'efficacité est lente, le code ultérieur commence à s'exécuter et se termine avant l'exécution du code précédent. Peu importe qui commence en premier, il est possible que le code suivant se termine en premier dans certains cas.

Au niveau matériel, le CPU réorganisera le lot d'instructions reçu selon ses règles. Ceci est également basé sur le fait que la vitesse du CPU est plus rapide que la vitesse du cache. Le but est similaire au précédent. point, sauf que pour le traitement matériel, chaque fois ne peut être réorganisé que dans la plage limitée d'instructions reçues, tandis que la machine virtuelle peut être réorganisée à un niveau plus large et dans une plage d'instructions plus large. Pour le mécanisme de réorganisation du matériel, veuillez vous référer à "Réorganisation de la mémoire CPU à partir de la concurrence JVM"

La réorganisation est difficile à comprendre. Ce qui précède mentionne simplement ses scénarios. Si vous souhaitez avoir une meilleure compréhension, ce concept nécessite la construction. de quelques exemples et graphiques. Voici deux articles plus détaillés et plus vivants "explication qui se produit avant" et "Compréhension approfondie du modèle de mémoire Java (2) - Réorganisation". Parmi eux, "comme si-série" doit être maîtrisé, c'est-à-dire : peu importe la façon dont il est réorganisé, le résultat de l'exécution du programme monothread ne peut pas être modifié. Les compilateurs, les environnements d'exécution et les processeurs doivent tous adhérer à la sémantique « comme si c'était en série ». Prenons un exemple simple,


public void execute(){
 int a=0;
 int b=1;
 int c=a+b;
}

Les deux phrases a=0 et b=1 peuvent être triées arbitrairement sans affecter le résultat logique du programme, mais c=a+ Cette phrase doit être exécuté après les deux premières phrases.

Comme vous pouvez le voir dans l'exemple précédent, la probabilité de réorganisation dans un environnement multithread est assez élevée. Les mots-clés volatile et synchronisé peuvent désactiver la réorganisation. De plus, il existe certaines règles, ce sont ces règles. cela nous évite de ressentir les inconvénients du réordonnancement dans notre travail de programmation quotidien.

Règle d'ordre des programmes : dans un thread, selon l'ordre du code, les opérations écrites au début se produisent avant les opérations écrites à l'arrière. Pour être précis, il devrait s'agir de la séquence de flux de contrôle plutôt que de la séquence de code, car des structures telles que des branches et des boucles doivent être prises en compte.

Règle de verrouillage du moniteur : une opération de déverrouillage se produit avant une opération de verrouillage ultérieure sur le même verrouillage d'objet. L'accent est ici mis sur le même verrou, et « plus tard » fait référence à la séquence temporelle, telle que les opérations de verrouillage qui se produisent dans d'autres threads.

Règle de variable volatile : une opération d'écriture dans une variable volatile se produit après une opération de lecture ultérieure sur la variable. Le "plus tard" fait également ici référence à la séquence dans le temps.

Règle de démarrage du fil : la méthode exclusive start() du fil précède chaque action de ce fil.

Règle de terminaison de thread : chaque opération dans un thread se produit en premier lors de la détection de terminaison de ce thread. Nous pouvons y mettre fin via la méthode Thread.join() et le retour de Thread.isAlive(). La valeur détecte cela. le thread a terminé son exécution.

Règle d'interruption de thread : L'appel à la méthode thread interrompu() a priorité sur le code du thread interrompu pour détecter l'occurrence de l'événement d'interruption. Vous pouvez utiliser la méthode Thread.interrupted() pour détecter si. le fil a été interrompu.

Règle du finaliseur : l'initialisation d'un objet (la fin de l'exécution du constructeur) se produit en premier au début de sa méthode finalize().

Transitivité : Si l'opération A se produit avant l'opération B et que l'opération B se produit avant l'opération C, alors nous pouvons conclure que l'opération A se produit avant l'opération C.

Ce sont les règles ci-dessus qui garantissent l'ordre d'arrivée avant. Si les règles ci-dessus ne sont pas respectées, alors dans un environnement multithread, il n'y a aucune garantie que l'ordre d'exécution soit égal à l'ordre du code, c'est-à-dire , "si vous observez ce thread, toutes les opérations sont en ordre ; si vous observez un autre thread dans un thread, tout ce qui ne respecte pas les règles ci-dessus est dans le désordre." Par conséquent, si notre programme multithread s'appuie sur l'ordre. d'écriture de code, nous devons alors déterminer s'il est conforme. Si les règles ci-dessus ne sont pas respectées, certains mécanismes doivent être utilisés pour les rendre conformes. Les plus couramment utilisés sont les modificateurs synchronisés, de verrouillage et volatiles.

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn