sábado
oct292011
Ejemplo de hilos 2.
package es.scjp; import java.util.Vector; /** * En esta clase vamos a intentar sincronizar dos hilos y un recurso entre sí * La idea es generar un productor y un consumidor que produzcan y lean elementos * @author jcarmonaloeches * @desarrolloj2ee (twitter) */ public class HilosEnJavaParte2 { public static void main(String args[]) { /*Recurso que compartiremos entre el productor y el consumidor*/ RecursoCompartido rc = new RecursoCompartido(); /*Creamos la instancia de productor y consumidor, esta instancia recibe el parámetro del recurso compartido*/ Productor productor = new Productor(rc); Consumidor consumidor = new Consumidor(rc); /*Iniciamos el productor y el consumidor. Esta llámada ejecuta el método run*/ productor.start(); consumidor.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } /*Damos la orden a los threads indicando que ya no están activos*/ productor.setEstaActivo(false); consumidor.setEstaActivo(false); } } /** * Hilo productor que inserta datos de manera sincronizada en un recurso compartido. * La variables estaActivo indica si el hilo está en ejecución. * @author Jaime Carmona Loeches * @desarrolloj2ee (twitter) */ class Productor extends Thread { private boolean estaActivo = true; private RecursoCompartido rc; public boolean isEstaActivo() { return estaActivo; } public void setEstaActivo(boolean estaActivo) { this.estaActivo = estaActivo; } public Productor(RecursoCompartido rc) { this.rc = rc; } public void run() { while (estaActivo) { rc.añadirDatos(); } } } /** * Hilo que recibe datos de un recurso compartido de manera sincronizada * * @author Jaime Carmona Loeches * @desarrolloj2ee (twitter) * */ class Consumidor extends Thread { private boolean estaActivo = true; private RecursoCompartido rc; public Consumidor(RecursoCompartido rc) { this.rc = rc; } public boolean isEstaActivo() { return estaActivo; } public void setEstaActivo(boolean estaActivo) { this.estaActivo = estaActivo; } public void run() { while (estaActivo) { rc.leerDatos(); } } } /** * Clase que permite gestionar un recurso. El estado indica 1 o 2, es decir, * leyendo o añadiendo. Los métodos se ejecutan de manera complementaria: o bien * ejecuta uno, o bien ejecuta otro. Al ser métodos sincronizados, un método * sólo puede ser ejecutado por un mismo hilo a la vez. Cuando un método termina * la ejecución indicará a los hilos la finalización de esta ejecución, y eso * permitirá saltar del estado wait al estado run. * * @author Jaime Carmona Loeches * @desarrolloj2ee */ class RecursoCompartido { private int estado = 2; private int contador = 0; private Vector recursoCompartido = new Vector(); public Vector getRecursoCompartido() { return recursoCompartido; } public void setRecursoCompartido(Vector recursoCompartido) { this.recursoCompartido = recursoCompartido; } /** * Metodo de añadido de datos, estado = 2 * Este método es sincronizado y no permite que dos hilos lo ejecuten a la vez. * En caso de que haya dos hilos que quieran ejecutar, la JVM * establece prioridades de ejecución, estableciendo una cola de espera */ public synchronized void añadirDatos() { /*Mientras el estado sea igual a 2, se establece una espera, que terminará cuando el estado cambie de valor*/ while (estado == 2) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * Cuando el estado cambia de valor, leemos los elementos e imprimimos la información */ StringBuffer elementosLeidos = new StringBuffer(); while (recursoCompartido.size() > 0) { elementosLeidos.append("*"+recursoCompartido.firstElement()); recursoCompartido.remove(0); } System.out.println("Consumido->" + elementosLeidos + "*"); /*Finalmente, modificamos el estados, y mediante notifyAll, notificamos al resto de hilos que esperaban a este método que esta ejecución ha finalizado*/ estado = 2; notifyAll(); } /** * Metodo de lectura de datos, estado = 1 */ public synchronized void leerDatos() { while (estado == 1) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * Añadimos 5 elementos y mantenemos la cuenta a nivel de instancia de * clase */ StringBuffer elementosGenerados = new StringBuffer(); int valorFinal = contador + 5; for (int i = contador; i < valorFinal; i++, contador++) { String numero = new Integer(i).toString(); elementosGenerados.append("*" + numero); this.getRecursoCompartido().add(numero); } elementosGenerados.append("*"); System.out.println("Producido->" + elementosGenerados); estado = 1; notifyAll(); } }En el código anterior, hemos generado una clase donde un productor y un consumidor comparten un recurso común, y el acceso al mismo es sincronizado mediante el uso de synchronized. El uso de synchronized que, en este caso, hemos aplicado a nivel de método, nos permite sincronizar el acceso por parte de dos hilos que desean ejecutar un trozo de código, y por motivos de integridad de datos, no es conveniente que lo ejecuten al mismo tiempo. Para hacernos una idea, cuando dos hilos intentan acceder a dicho método, y uno de ellos está en ejecución, el otro hilo queda en estado de espera, esperando a que el hilo que llego antes que él termine la ejecución. Este hilo, al terminar la ejecución, debe notificar de alguna manera al hilo que está esperando que dicha ejecución ya ha terminado. Por eso se hace uso de la llamada notifyAll() que permite notificar a los hilos que dicha ejecución ha terminado y el recurso (o el método) en este caso ha quedado liberado.
Reader Comments