Un escenario simple usando wait () y notify () en java

¿Puedo obtener un escenario simple completo, es decir, un tutorial que sugiera cómo se debe usar, específicamente con una cola?

    Los métodos wait() y notify() están diseñados para proporcionar un mecanismo que permita bloquear un hilo hasta que se cumpla una condición específica. Para esto, supongo que quieres escribir una implementación de cola de locking, donde tienes un 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, querrá que el método put() se bloquee hasta que haya espacio libre en la tienda, y querrá que el método take() se bloquee hasta que haya algún elemento que devolver.

     public class BlockingQueue { private Queue queue = new LinkedList(); 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 que debe tener en cuenta sobre la forma en que debe usar los mecanismos de esperar y notificar.

    En primer lugar, debe asegurarse de que las llamadas a wait() o notify() encuentren 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 hilos estándar) se debe a algo conocido como señal perdida.

    Un ejemplo de esto es que un hilo puede llamar a put() cuando la cola está llena, luego verifica la condición, ve que la cola está llena, sin embargo, antes de poder bloquear, se progtwig otro hilo. Este segundo subproceso luego take() un elemento de la cola y notifica a los subprocesos en espera que la cola ya no está llena. Sin embargo, como el primer hilo ya ha verificado la condición, simplemente llamará a wait() después de haber sido reprogtwigdo, aunque podría progresar.

    Al sincronizar en un objeto compartido, puede asegurarse de que este problema no ocurra, ya que la llamada a la take() del segundo subproceso no podrá avanzar hasta que el primer subproceso haya bloqueado realmente.

    En segundo lugar, debe colocar la condición que está comprobando en un ciclo while, en lugar de una instrucción if, debido a un problema conocido como wake-ups falsos. Aquí es donde a veces se puede volver a activar un hilo de espera sin que se llame a notify() . Al poner esta comprobación en un ciclo while se asegurará de que si se produce un despertar falso, se volverá a verificar la condición y el hilo volverá a llamar a wait() .


    Como algunas de las otras respuestas han mencionado, Java 1.5 introdujo una nueva biblioteca de concurrencia (en el paquete java.util.concurrent ) que fue diseñada para proporcionar una abstracción de mayor nivel sobre el mecanismo de esperar / notificar. Usando estas nuevas características, podría reescribir el ejemplo original de la siguiente manera:

     public class BlockingQueue { private Queue queue = new LinkedList(); 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 locking, entonces 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 podría desear saber sobre problemas y soluciones relacionados con concurrencia.

    No es un ejemplo de cola, pero es extremadamente simple 🙂

     class MyHouse { private boolean pizzaArrived = false; public void eatPizza(){ synchronized(this){ while(!pizzaArrived){ wait(); } } System.out.println("yumyum.."); } public void pizzaGuy(){ synchronized(this){ this.pizzaArrived = true; notifyAll(); } } } 

    Algunos puntos importantes:
    1) NUNCA hacer

      if(!pizzaArrived){ wait(); } 

    Siempre use mientras (condición), porque

    • a) los hilos pueden despertarse esporádicamente desde el estado de espera sin que nadie los notifique. (incluso cuando el tipo de pizza no sonaba, alguien decidía probar la pizza).
    • b) Debería verificar la condición nuevamente después de adquirir el locking sincronizado. Digamos que la pizza no dura para siempre. Usted despierta, alinee para la pizza, pero no es suficiente para todos. Si no revisas, ¡podrías comer papel! 🙂 (probablemente el mejor ejemplo sería while(!pizzaExists){ wait(); } .

    2) Debe mantener el locking (sincronizado) antes de invocar wait / nofity. Los hilos también deben adquirir locking antes de despertarse.

    3) Trate de evitar adquirir cualquier locking dentro de su bloque sincronizado y trate de no invocar métodos alienígenas (métodos que no sabe con certeza de lo que están haciendo). Si es necesario, asegúrese de tomar medidas para evitar interlockings.

    4) Tenga cuidado con notify (). Quédese con notifyAll () hasta que sepa lo que está haciendo.

    5) ¡Por último, pero no menos importante, lea Java Concurrency in Practice !

    A pesar de que solicitó wait() y notify() específicamente, creo que esta cita todavía es lo suficientemente importante:

    Josh Bloch, Effective Java 2nd Edition , Item 69: Prefiere utilidades de simultaneidad para wait y notify (énfasis en él):

    Dada la dificultad de usar wait y notify correctamente, debe usar las utilidades de concurrencia de mayor nivel, en cambio usar […] wait y notify directamente es como progtwigr en “lenguaje ensamblador concurrencia”, en comparación con el lenguaje de nivel superior proporcionado por java.util.concurrent . Rara vez, si alguna vez, razón para usar wait y notify en un nuevo código .

    ¿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 un software real. Es bueno jugar con él para que sepas qué es, pero la concurrencia tiene dificultades en todo el lugar. 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 de manejar los hilos a mano siempre que sea posible.

    Ejemplo

     public class myThread extends Thread{ @override public void run(){ while(true){ threadCondWait();// Circle waiting... //bla bla bla bla } } public synchronized void threadCondWait(){ while(myCondition){ wait();//Comminucate with notify() } } } public class myAnotherThread extends Thread{ @override public void run(){ //Bla Bla bla notify();//Trigger wait() Next Step } } 

    Ejemplo para wait () y notifyall () en Threading.

    Una lista de matriz estática sincronizada se utiliza como recurso y se llama al método wait () si la lista de arreglos está vacía. El método notify () se invoca una vez que se agrega un elemento para la lista de arreglos.

     public class PrinterResource extends Thread{ //resource public static List arrayList = new ArrayList(); public void addElement(String a){ //System.out.println("Add element method "+this.getName()); synchronized (arrayList) { arrayList.add(a); arrayList.notifyAll(); } } public void removeElement(){ //System.out.println("Remove element method "+this.getName()); synchronized (arrayList) { if(arrayList.size() == 0){ try { arrayList.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else{ arrayList.remove(0); } } } public void run(){ System.out.println("Thread name -- "+this.getName()); if(!this.getName().equalsIgnoreCase("p4")){ this.removeElement(); } this.addElement("threads"); } public static void main(String[] args) { PrinterResource p1 = new PrinterResource(); p1.setName("p1"); p1.start(); PrinterResource p2 = new PrinterResource(); p2.setName("p2"); p2.start(); PrinterResource p3 = new PrinterResource(); p3.setName("p3"); p3.start(); PrinterResource p4 = new PrinterResource(); p4.setName("p4"); p4.start(); try{ p1.join(); p2.join(); p3.join(); p4.join(); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("Final size of arraylist "+arrayList.size()); } }