Pourquoi HotSpot optimise-t-il les éléments suivants en utilisant le levage?

Dans “Effective Java”, l’auteur a mentionné que

while (!done) i++; 

peut être optimisé par HotSpot en

 if (!done) { while (true) i++; } 

Je suis très confus à ce sujet. La variable done n’est généralement pas une constante , pourquoi le compilateur peut-il optimiser cela?

L’auteur suppose que la variable done est une variable locale, qui n’a aucune exigence dans le modèle de mémoire Java pour exposer sa valeur à d’autres threads sans primitives de synchronisation. Ou dit d’une autre manière: la valeur de done ne sera pas modifiée ou visualisée par un code autre que ce qui est montré ici.

Dans ce cas, puisque la boucle ne modifie pas la valeur de done , sa valeur peut être ignorée et le compilateur peut évaluer l’évaluation de cette variable en dehors de la boucle, ce qui l’empêche d’être évalué dans la partie “hot” du fichier. boucle. Cela rend la boucle plus rapide car elle doit faire moins de travail.

Cela fonctionne également dans des expressions plus complexes, telles que la longueur d’un tableau:

 int[] array = new int[10000]; for (int i = 0; i < array.length; ++i) { array[i] = Random.nextInt(); } 

Dans ce cas, l'implémentation naïve évaluerait la longueur du tableau 10 000 fois, mais comme le tableau de variables n'est jamais affecté et que la longueur du tableau ne changera jamais, l'évaluation peut changer pour:

 int[] array = new int[10000]; for (int i = 0, $l = array.length; i < $l; ++i) { array[i] = Random.nextInt(); } 

D'autres optimisations s'appliquent également ici sans rapport avec le levage.

J'espère que cela pourra aider.

 public class StopThread { private static boolean stopRequested; private static synchronized void requestStop() { stopRequested = true; } private static synchronized boolean stopRequested() { return stopRequested; } public static void main(Ssortingng[] args) throws InterruptedException { Thread backgroundThread = new Thread(new Runnable() { public void run() { int i = 0; while (!stopRequested()) i++; } }); backgroundThread.start(); TimeUnit.SECONDS.sleep(1); requestStop(); } } 

le code ci-dessus est correct dans le code effectif , cela revient à utiliser volatile pour décorer le stopRequested .

 private static boolean stopRequested() { return stopRequested; } 

Si cette méthode omet le mot clé synchronized , ce programme ne fonctionne pas bien.
Je pense que ce changement provoque le levage lorsque la méthode omet le mot clé synchronized .

Si vous ajoutez System.out.println("i = " + i); dans la boucle while. Le levage ne fonctionnera pas, ce qui signifie que le programme s’arrête comme prévu. La méthode println est thread-safe afin que le jvm ne puisse pas optimiser le segment de code?