Este artículo contiene dos ejemplos de código para demostrar la simultaneidad de Java. Representan un uso muy típico. Al comprenderlos, comprenderá mejor las notificaciones () y la espera ().
1. Algunos conocimientos previos
-
synchronized
La palabra clave se utiliza para acceso exclusivo. - Para sincronizar un método, simplemente agregue la palabra clave sincronizada a su declaración. Entonces no se pueden intercalar dos invocaciones de métodos sincronizados en el mismo objeto.
- Las declaraciones sincronizadas deben especificar el objeto que proporciona el bloqueo intrínseco. Cuando se utiliza sincronizado (esto), debe evitar sincronizar las invocaciones de los métodos de otros objetos.
-
wait()
le dice al hilo de llamada que abandone el monitor y se vaya a dormir hasta que otro hilo entre en el mismo monitor y llame anotificar (). -
notify()
despierta el primer hilo que llamówait()
en el mismo objeto.
2. notificar () y esperar () – ejemplo 1
public class ThreadA { public static void main(String[] args){ ThreadB b = new ThreadB(); b.start(); synchronized(b){ try{ System.out.println("Waiting for b to complete..."); b.wait(); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("Total is: " + b.total); } } } class ThreadB extends Thread{ int total; @Override public void run(){ synchronized(this){ for(int i=0; i<100 ; i++){ total += i; } notify(); } } } |
En el ejemplo anterior, un objeto, b, está sincronizado. b completa el cálculo antes de que el hilo principal emita su valor total.
Producción:
Waiting for b to complete... Total is: 4950
Si b no está sincronizado como el siguiente código:
public class ThreadA { public static void main(String[] args) { ThreadB b = new ThreadB(); b.start(); System.out.println("Total is: " + b.total); } } class ThreadB extends Thread { int total; @Override public void run() { for (int i = 0; i < 100; i++) { total += i; } } } |
El resultado sería 0, 10, etc. Porque la suma no se termina antes de usarse.
3. notificar () y esperar () – ejemplo 2
El segundo ejemplo es más complejo, vea los comentarios.
import java.util.Vector; class Producer extends Thread { static final int MAXQUEUE = 5; private Vector messages = new Vector(); @Override public void run() { try { while (true) { putMessage(); //sleep(5000); } } catch (InterruptedException e) { } } private synchronized void putMessage() throws InterruptedException { while (messages.size() == MAXQUEUE) { wait(); } messages.addElement(new java.util.Date().toString()); System.out.println("put message"); notify(); //Later, when the necessary event happens, the thread that is running it calls notify() from a block synchronized on the same object. } // Called by Consumer public synchronized String getMessage() throws InterruptedException { notify(); while (messages.size() == 0) { wait();//By executing wait() from a synchronized block, a thread gives up its hold on the lock and goes to sleep. } String message = (String) messages.firstElement(); messages.removeElement(message); return message; } } class Consumer extends Thread { Producer producer; Consumer(Producer p) { producer = p; } @Override public void run() { try { while (true) { String message = producer.getMessage(); System.out.println("Got message: " + message); //sleep(200); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String args[]) { Producer producer = new Producer(); producer.start(); new Consumer(producer).start(); } } |
Una posible secuencia de salida:
Got message: Fri Dec 02 21:37:21 EST 2011 put message put message put message put message put message Got message: Fri Dec 02 21:37:21 EST 2011 Got message: Fri Dec 02 21:37:21 EST 2011 Got message: Fri Dec 02 21:37:21 EST 2011 Got message: Fri Dec 02 21:37:21 EST 2011 Got message: Fri Dec 02 21:37:21 EST 2011 put message put message put message put message put message Got message: Fri Dec 02 21:37:21 EST 2011 Got message: Fri Dec 02 21:37:21 EST 2011 Got message: Fri Dec 02 21:37:21 EST 2011