En ArrayBlockingQueue, ¿por qué copiar el campo de miembro final en la variable final local?

81

En ArrayBlockingQueue, todos los métodos que requieren el bloqueo lo copian en una finalvariable local antes de llamar 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();
    }
}

¿Hay alguna razón para copiar this.locka una variable local lockcuando el campo this.lockes final?

Además, también usa una copia local de E[]antes de actuar sobre él:

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

¿Existe alguna razón para copiar un campo final a una variable final local?

mjlee
fuente

Respuestas:

68

Es una optimización extrema que le gusta usar a Doug Lea, el autor de la clase. Aquí hay una publicación en un hilo reciente en la lista de correo core-libs-dev sobre este tema exacto que responde bastante bien a su pregunta.

del post:

... copiar a locales produce el código de bytes más pequeño, y para el código de bajo nivel es bueno escribir código que esté un poco más cerca de la máquina

ColinD
fuente
15
¡Fuerte énfasis en "extremo"! Esta no es una buena práctica de programación de propósito general que todos deberían emular.
Kevin Bourrillion
15
Para su información aleatoria: en algunos otros casos, cuando ve que esto se hace, es porque el campo en cuestión es volátil y el método debe asegurarse de que tenga un único valor consistente o referencia para él en todo momento.
Kevin Bourrillion
2
Tomaré esta optimización "extrema" en una clase básica como esta.
Erick Robertson
4
@zamza, las variables finales locales solo las usa el compilador java, no el código de bytes (es decir, JVM no sabe si una variable local es final)
bestsss
1
Además del tamaño del código de bytes, ¿es esto una optimización también para la velocidad de ejecución?
SantiBailors
13

Este hilo da algunas respuestas. En sustancia:

  • el compilador no puede probar fácilmente que un campo final no cambia dentro de un método (debido a la reflexión / serialización, etc.)
  • la mayoría de los compiladores actuales en realidad no lo intentan y, por lo tanto, tendrían que volver a cargar el campo final cada vez que se usa, lo que podría provocar una falla de caché o una falla de página
  • almacenarlo en una variable local obliga a la JVM a realizar solo una carga
Assylias
fuente
2
No creo que finalla JVM deba recargar una variable. Si modifica una finalvariable a través de la reflexión, pierde la garantía de que su programa funcione correctamente (lo que significa que el nuevo valor podría no tenerse en cuenta en todos los casos).
icza