He estado leyendo en el libro "Concurrency Programming in Java" de Doug Lea. Como sabrá, Doug escribió originalmente la API Java Concurrency. Sin embargo, algo me ha causado cierta confusión y esperaba obtener algunas opiniones sobre este pequeño enigma.Sincronización de una cola
Tome el siguiente código de ejemplo cola de Doug Lea ...
class LinkedQueue {
protected Node head = new Node(null);
protected Node last = head;
protected final Object pollLock = new Object();
protected final Object putLock = new Object();
public void put(Object x) {
Node node = new Node(x);
synchronized (putLock) { // insert at end of list
synchronized (last) {
last.next = node; // extend list
last = node;
}
}
}
public Object poll() { // returns null if empty
synchronized (pollLock) {
synchronized (head) {
Object x = null;
Node first = head.next; // get to first real node
if (first != null) {
x = first.object;
first.object = null; // forget old object
head = first; // first becomes new head
}
return x;
}
}
}
static class Node { // local node class for queue
Object object;
Node next = null;
Node(Object x) { object = x; }
}
}
Este bastantes un buen cola. Utiliza dos monitores para que un Productor y un Consumidor puedan acceder a la cola al mismo tiempo. ¡Bonito! Sin embargo, la sincronización en 'último' y 'cabeza' me confunde aquí. El libro indica que esto es necesario para la situación en la que Queue está actualmente o a punto de tener 0 entradas. Ok, es justo y este tipo de tiene sentido.
Sin embargo, luego miré Java Concurrency LinkedBlockingQueue. La versión original de la cola no se sincroniza en la cabeza o la cola (también quería publicar otro enlace a la versión moderna que también sufre el mismo problema, pero no pude hacerlo porque soy novato). Me pregunto por qué no? ¿Me estoy perdiendo de algo? ¿Hay alguna parte de la naturaleza idiosincrásica del Modelo de memoria de Java que me falta? ¿Habría pensado con fines de visibilidad que esta sincronización es necesaria? ¡Agradecería algunas opiniones de expertos!
Tenga en cuenta que mi teoría actual reside en que el método 'signalNotEmpty' ingresa al bloque sincronizado para 'takeLock'. Esto tal vez obligaría a la 'cabeza' a ser vista. –