jueves, 21 de julio de 2011

Trabajando con hilos

Hoy voy a poner un ejemplo de hilos con java, vamos a empezar con un poco de teoría, para crear hilos con java hay dos opciones:
  • Extender de la clase Thread.
  • Implementar la interfaz Runnable.
Mi recomendación es implementar la Interfaz Runnable, ¿Porque? si queremos hacer un pool de hilos para un interfaz las clases tiene que implementar la interfaz Runable. En cualquier caso la opción que elijamos la lógica tiene que escribirse en el método 'run()'. Voy a poner un Ejemplo típico de Productor y Consumidor para ver como crear hilos, despues vamos hacer que sincronizen los hilos para que el productor no ponga  más datos en el buffer cuando este lleno y el consumidor no puedar coger datos cuando esta vacia.

Empezamos vamos a crear un interfaz que se llama Buffer este es su código.

/**
 * Interfaz que permite crear un buffer entre el consumidor y productor
 * @author Xabe
 */
public interface Buffer {
 /**
  * Ponemos un valor en el Buffer
  * @param value
  */
 public void putValue(T value);
 
 /**
  * Obtenemos el valor generico
  * @return 
  */
 public T getValue();
 
 /**
  * Muestra un mensaje de información sobre quien pone un valor
  * @param value
  */
 public void messagePut(T value);
 
 /**
  * Muestra un mensaje de información sobre quien obtiene un valor
  * @param value
  */
 public void messageGet(T value);
}

Ahora vamos implementar el buffer uno sincronizado y otro sin sincronizado.

/**
 * Buffer sin sincronizar
 * @author xabe
 *
 */
public class BufferSinSinconizar implements Buffer {
 private Integer lista[];
 
 /**
  * Construye un array de un elemento para almacenar los datos
  */
 public BufferSinSinconizar() {
  lista = new Integer[] {-1};
 }
 
 
 public Integer getValue() {
  Integer value = lista[0];
  lista[0] = -1;
  messageGet(value);
  return value;
 }

 public void putValue(Integer value) {
  messagePut(value);
  lista[0] = value;
 }
 
 public void messageGet(Integer value) {
  System.out.println("El hilo : "+Thread.currentThread().getName()+" obtiene el valor "+value);
  
 }
 public void messagePut(Integer value) {
  System.out.println("El hilo : "+Thread.currentThread().getName()+" pone el valor "+value);
 }
}
 
La clase buffer sincronizado.
public class BufferSincronizado implements Buffer {
 private Integer lista[];

 public BufferSincronizado() {
  lista = new Integer[] { -1, -1 };
 }

 public synchronized Integer getValue() {
  while (IsBufferVacio()) {
   try {
    System.out.println(Thread.currentThread().getName() + " trata de leer.");
    System.out.println("Bufer vacio.");
    wait();
   }
   // si se interrumpió el subproceso en espera, imprimir el rastreo de
   // la pila
   catch (InterruptedException excepcion) {
    excepcion.printStackTrace();
   }
  }
  int value = -1;
  for (int i = 0; i < lista.length; i++) {
   if (lista[i] != -1) {
    value = lista[i];
    lista[i] = -1;
    break;
   }
  }
  notify();
  messageGet(value);
  return value;
 }

 public synchronized void putValue(Integer value) {
  while (IsBufferLleno()) {
   try {
    System.out.println(Thread.currentThread().getName()
      + " trata de escribir.");
    System.out.println("El Bufer esta lleno.");
    wait();
   } catch (InterruptedException excepcion) {
    excepcion.printStackTrace();
   }
  }
  for (int i = 0; i < lista.length; i++) {
   if (lista[i] == -1) {
    lista[i] = value;
    break;
   }
  }
  messagePut(value);
  notify();
 }

 public boolean IsBufferLleno() {
  boolean lleno = true;
  for (Integer valor : lista) {
   if (valor == -1) {
    lleno = false;
    break;
   }
  }
  return lleno;
 }
 
 public boolean IsBufferVacio() {
  boolean vacio = true;
  for (Integer valor : lista) {
   if (valor > -1) {
    vacio = false;
    break;
   }
  }
  return vacio;
 }

 public void messageGet(Integer value) {
  System.out.println("El hilo : " + Thread.currentThread().getName()
    + " obtiene el valor " + value);

 }

 public void messagePut(Integer value) {
  System.out.println("El hilo : " + Thread.currentThread().getName()
    + " pone el valor " + value);
 }
}
Ahora vamos a poner el código de Consumidor y Productor
public class Consumidor implements Runnable {
 private Thread thread;
 private Buffer buffer;

 public Consumidor(String name, Buffer buffer) {
  this.thread = new Thread(this, name);
  this.buffer = buffer;
 }

 public void run() {
  int suma = 0;
  try {
   for (int cuenta = 1; cuenta <= 4; cuenta++) {
    Thread.sleep((int) (Math.random() * 3001));
    suma += buffer.getValue();
   }
  } catch (InterruptedException excepcion) {
   excepcion.printStackTrace();
  }
  System.out
    .println(thread.getName()
      + " leyo valores, dando un total de: " + suma
      + ".\nTerminando");
 }

 public void start() {
  this.thread.start();
 }

}

public class Productor implements Runnable {
 private Thread thread;
 private Buffer buffer;

 public Productor(String name, Buffer buffer) {
  this.thread = new Thread(this, name);
  this.buffer = buffer;
 }

 public void run() {
  try {
   for (int cuenta = 1; cuenta <= 4; cuenta++) {
    Thread.sleep((int) (Math.random() * 3001));
    buffer.putValue(cuenta);
   }
  } catch (InterruptedException excepcion) {
   excepcion.printStackTrace();
  }
  System.out
  .println(thread.getName()
    + " puso valores"
    + ".\nTerminando");
 }

 public void start() {
  this.thread.start();
 }
}
Por ultimo la clase Main que invoca a los hilos
public static void main(String[] args) {
  Buffer buffer = new BufferSincronizado();
//  Buffer buffer = new BufferSinSinconizar();
  Consumidor consumidor = new Consumidor("Consumidor", buffer);
  Productor productor = new Productor("Productor", buffer);
  productor.start();
  consumidor.start();
 }
Con este ejemplo vemos como se crea hilos y como sincronizar los hilos.

No hay comentarios:

Publicar un comentario