¿Puedo obtener un escenario simple y completo, es decir, un tutorial que sugiera cómo se debe usar esto, específicamente con una cola?
Los métodos wait()
y notify()
están diseñados para proporcionar un mecanismo que permita que un subproceso se bloquee hasta que se cumpla una condición específica. Para esto, supongo que desea escribir una implementación de cola de bloqueo, donde tenga algún almacén de elementos de respaldo de tamaño fijo.
Lo primero que debe hacer es identificar las condiciones que desea que esperen los métodos. En este caso, deseará que el put()
método se bloquee hasta que haya espacio libre en la tienda, y deseará que el take()
método se bloquee hasta que haya algún elemento que devolver.
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T element) throws InterruptedException {
while(queue.size() == capacity) {
wait();
}
queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
}
public synchronized T take() throws InterruptedException {
while(queue.isEmpty()) {
wait();
}
T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
return item;
}
}
Hay algunas cosas a tener en cuenta sobre la forma en que debe utilizar los mecanismos de espera y notificación.
En primer lugar, debe asegurarse de que cualquier llamada wait()
ao notify()
esté dentro de una región de código sincronizada (con las llamadas wait()
y notify()
sincronizadas en el mismo objeto). La razón de esto (aparte de las preocupaciones de seguridad de hilo estándar) se debe a algo conocido como una señal perdida.
Un ejemplo de esto es que un subproceso puede llamar put()
cuando la cola está llena, luego verifica la condición, ve que la cola está llena, sin embargo, antes de que pueda bloquear otro subproceso está programado. Este segundo subproceso take()
es un elemento de la cola y notifica a los subprocesos en espera que la cola ya no está llena. Sin embargo, debido a que el primer subproceso ya ha verificado la condición, simplemente llamará wait()
después de ser reprogramado, a pesar de que podría avanzar.
Al sincronizar un objeto compartido, puede asegurarse de que este problema no ocurra, ya que la take()
llamada del segundo subproceso no podrá avanzar hasta que el primer subproceso se haya bloqueado realmente.
En segundo lugar, debe colocar la condición que está verificando en un ciclo while, en lugar de una declaración if, debido a un problema conocido como despertadores espurios. Aquí es donde un hilo de espera a veces se puede reactivar sin notify()
ser llamado. Al poner esta verificación en un bucle while, se garantizará que si se produce una activación espuria, la condición se volverá a verificar y el hilo volverá a llamar wait()
.
Como algunas de las otras respuestas han mencionado, Java 1.5 introdujo una nueva biblioteca de concurrencia (en el java.util.concurrent
paquete) que fue diseñada para proporcionar una abstracción de nivel superior sobre el mecanismo de espera / notificación. Con estas nuevas funciones, podría reescribir el ejemplo original de esta manera:
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void put(T element) throws InterruptedException {
lock.lock();
try {
while(queue.size() == capacity) {
notFull.await();
}
queue.add(element);
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while(queue.isEmpty()) {
notEmpty.await();
}
T item = queue.remove();
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}
Por supuesto, si realmente necesita una cola de bloqueo, debe usar una implementación de la interfaz BlockingQueue .
Además, para cosas como esta, recomiendo Java Concurrency in Practice , ya que cubre todo lo que pueda desear saber sobre problemas y soluciones relacionados con la concurrencia.
notify
solo despierta un hilo. Si dos subprocesos del consumidor compiten para eliminar un elemento, una notificación puede despertar al otro subproceso del consumidor, que no puede hacer nada al respecto y volverá a dormir (en lugar del productor, que esperábamos insertar un nuevo elemento). el hilo productor no se despierta, no se inserta nada y ahora los tres hilos dormirán indefinidamente. Eliminé mi comentario anterior porque decía (erróneamente) que el despertar espurio era la causa del problema (no lo es)No es un ejemplo de cola, pero es extremadamente simple :)
Algunos puntos importantes:
1) NUNCA
Siempre use while (condición), porque
while(!pizzaExists){ wait(); }
.2) Debe mantener el bloqueo (sincronizado) antes de invocar wait / nofity. Los hilos también deben adquirir el bloqueo antes de despertarse.
3) Intente evitar adquirir cualquier bloqueo dentro de su bloque sincronizado y trate de no invocar métodos extraños (métodos que no sabe con certeza lo que están haciendo). Si es necesario, asegúrese de tomar medidas para evitar puntos muertos.
4) Tenga cuidado al notificar (). Quédese con notifyAll () hasta que sepa lo que está haciendo.
5) ¡Por último, pero no menos importante, lea la concurrencia de Java en la práctica !
fuente
pizzaArrived
bandera? Si la bandera se cambia sin una llamadanotify
, no tendrá ningún efecto. También solo conwait
ynotify
llama el ejemplo funciona.synchronized
palabra clave, es redundante declarar la variablevolatile
, y se recomienda evitarla para evitar confusiones @mridaAunque lo solicitó
wait()
ynotify()
específicamente, creo que esta cita sigue siendo lo suficientemente importante:Josh Bloch, Effective Java 2nd Edition , Item 69: Prefiere las utilidades de concurrencia
wait
ynotify
(énfasis suyo):fuente
notify()
y elwait()
otro¿Has echado un vistazo a este tutorial de Java ?
Además, te aconsejo que te mantengas alejado de jugar con este tipo de cosas en software real. Es bueno jugar con él para que sepas lo que es, pero la concurrencia tiene dificultades por todas partes. Es mejor utilizar abstracciones de nivel superior y colecciones sincronizadas o colas JMS si está creando software para otras personas.
Eso es al menos lo que hago. No soy un experto en concurrencia, así que me mantengo alejado del manejo de hilos a mano siempre que sea posible.
fuente
Ejemplo
fuente
Ejemplo para wait () y notifyall () en Threading.
Se utiliza una lista de matriz estática sincronizada como recurso y se llama al método wait () si la lista de matriz está vacía. El método notify () se invoca una vez que se agrega un elemento para la lista de la matriz.
fuente
if(arrayList.size() == 0)
, creo que podría ser un error aquí.