Dans ArrayBlockingQueue, pourquoi copier le champ membre final dans la variable finale locale?

Dans ArrayBlockingQueue , toutes les méthodes nécessitant le verrou le copient dans une variable final locale avant d’appeler lock() .

 public boolean offer(E e) { if (e == null) throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); try { if (count == items.length) return false; else { insert(e); return true; } } finally { lock.unlock(); } } 

Y a-t-il une raison de copier this.lock vers un lock variable locale lorsque le champ this.lock est final ?

De plus, il utilise également une copie locale de E[] avant d’agir dessus:

 private E extract() { final E[] items = this.items; E x = items[takeIndex]; items[takeIndex] = null; takeIndex = inc(takeIndex); --count; notFull.signal(); return x; } 

Y a-t-il une raison de copier un champ final dans une variable finale locale?

C’est une optimisation extrême que Doug Lea, l’auteur de la classe, aime utiliser. Voici un article sur un sujet récent sur la liste de diffusion core-libs-dev à propos de ce sujet précis qui répond assez bien à votre question.

de la poste:

… copier vers les locaux produit le plus petit bytecode, et pour le code de bas niveau, il est intéressant d’écrire un code un peu plus proche de la machine

Ce fil donne des réponses. En substance:

  • le compilateur ne peut pas facilement prouver qu’un champ final ne change pas dans une méthode (à cause de reflection / sérialisation, etc.)
  • la plupart des compilateurs actuels n’essayent pas réellement et devraient donc recharger le champ final à chaque fois qu’il est utilisé, ce qui pourrait conduire à un échec du cache ou à une faute de page
  • le stocker dans une variable locale oblige la JVM à effectuer une seule charge