In ArrayBlockingQueue, perché copiare il campo del membro finale nella variabile finale locale?

In ArrayBlockingQueue , tutti i metodi che richiedono il lock lo copiano in una variabile final locale prima di chiamare 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(); } } 

C’è qualche motivo per copiare this.lock in un lock variabili locali quando il campo this.lock è final ?

Inoltre, usa anche una copia locale di E[] prima di agire su di esso:

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

C’è qualche motivo per copiare un campo finale su una variabile finale locale?

    È un’ottimizzazione estrema che piace a Doug Lea, l’autore della class. Ecco un post su una discussione recente sulla mailing list core-libs-dev su questo argomento esatto che risponde abbastanza bene alla tua domanda.

    dal post:

    … copiare i locals produce il bytecode più piccolo, e per il codice di basso livello è bello scrivere il codice che è un po ‘più vicino alla macchina

    Questa discussione fornisce alcune risposte. In sostanza:

    • il compilatore non può facilmente dimostrare che un campo finale non cambia all’interno di un metodo (a causa di riflessione / serializzazione ecc.)
    • i compilatori più attuali in realtà non tentano e dovrebbero pertanto ricaricare il campo finale ogni volta che viene utilizzato, il che potrebbe portare a un errore di cache o a un errore di pagina
    • memorizzandolo in una variabile locale forza la JVM a eseguire solo un carico