Pourquoi le code source JDK prend-il une copie finale des instances volatiles?

J’ai lu le code source du JDK sur ConcurrentHashMap.

Mais le code suivant m’a confondu:

public boolean isEmpty() { final Segment[] segments = this.segments; ... } 

Ma question est:

“this.segments” est déclaré:

 final Segment[] segments; 

Donc, ici, au début de la méthode, déclaré une même référence de type, pointez sur la même mémoire.

Pourquoi l’auteur l’a-t-il écrit comme ça? Pourquoi n’ont-ils pas utilisé directement ces segments? Y a-t-il une raison?

Ceci est un idiome typique pour le code sans locking impliquant volatile variables volatile . À la première ligne, vous lisez le volatile une fois, puis vous travaillez avec lui. En attendant, un autre thread peut mettre à jour le volatile , mais vous n’êtes intéressé que par la valeur initiale.

De plus, même lorsque la variable membre en question n’est pas volatile mais finale, cet idiome concerne les caches de CPU car la lecture depuis un emplacement de stack est plus adaptée au cache que la lecture depuis un emplacement de stack aléatoire. Il y a également une plus grande chance que la var locale soit liée à un registre de CPU.

Pour ce dernier cas, il y a en fait une certaine controverse, car le compilateur JIT va généralement s’occuper de ces problèmes, mais Doug Lea est l’un des gars qui s’en tient au principe général.

Je suppose que c’est pour la considération des performances, de sorte que nous avons seulement besoin de récupérer la valeur du champ une fois.

Vous pouvez vous référer à un singleton idiom de Java efficace par Joshua Bloch

Son singleton est ici:

 private volatile FieldType field; FieldType getField() { FieldType result = field; if (result == null) { synchronized(this) { result = field; if (result == null) field = result = computeFieldValue(); } } return result; } 

et il a écrit:

Ce code peut sembler un peu compliqué. En particulier, la nécessité du résultat de la variable locale peut ne pas être claire. Ce que fait cette variable est de s’assurer que le champ est lu une seule fois dans le cas commun où il est déjà initialisé. Bien que cela ne soit pas ssortingctement nécessaire, cela peut améliorer les performances et est plus élégant selon les normes appliquées à la programmation concurrente de bas niveau. Sur ma machine, la méthode ci-dessus est environ 25% plus rapide que la version évidente sans variable locale .

Cela peut réduire la taille du code d’octet – l’access à une variable locale est plus court dans le code d’octet que l’access à une variable d’instance.

L’optimisation de l’exécution peut également être réduite.

Mais rien de tout cela n’est significatif. C’est plus sur le style de code. Si vous vous sentez à l’aise avec les variables d’instance, certainement. Doug Lea se sent probablement plus à l’aise avec les variables locales.