gentoo linux, java, software libre y otras hierbas
dic, 20 2007 - 11:31 am

[código] Problema Productor-Consumidor: sincronización de Hilos en Java (1)

La entrada anterior de programación multihilo (o concurrente), contenía un ejemplo en el que se programaban hilos (o threads) que se ejecutaban independientemente. Sin embargo, existen muchas situaciones interesantes donde ejecutar threads concurrentes que compartan datos y deban considerar el estado y actividad de otros threads. Este conjunto de situaciones de programación son conocidos como escenarios ‘productor/consumidor’; donde el productor genera un canal de datos que es consumido por el consumidor.

Algunos lenguajes de programación, como Java, proporcionan una característica al trabajar con threads: la sincronización; esto permite, valga la redundancia, sincronizar los procesos para que trabajen de manera correcta.

En esta primera entrada veremos un ejemplo de Productor-Consumidor, en el que NO se utiliza la sincronización, con el fin de entender mejor los problemas que se presentan cuando los subprocesos “no se ponen de acuerdo”.

Ejemplo del algoritmo Productor-Consumidor SIN sincronización

En nuestro ejemplo, Productor y Consumidor comparten un Bufer; Productor escribe un dato en el Bufer, mientras que Consumidor lee un dato del Bufer.

Lo primero a implementar en este ejemplo es la interfaz Bufer.java, que contienen dos métodos: establecer(int), que es usada por el Productor para asignar un valor a Bufer; y obtener(), usada por el Consumidor para obtener el valor de Bufer:

// La interfaz Bufer especifica los métodos llamados por el Productor y el Consumidor.
public interface Bufer {
   public void establecer( int valor );  // colocar valor en Bufer
   public int obtener();              // devolver valor de Bufer
}

En nuestro ejemplo tenemos a la clase BuferNoSincronizado.java que implementa la interfaz Bufer.java, y que será la clase que compartirán el Productor y el Consumidor.

// BuferNoSincronizado representa a un solo entero compartido.
public class BuferNoSincronizado implements Bufer {
   private int bufer = -1; // compartido por los subprocesos productor y consumidor
   // colocar valor en bufer
   public void establecer( int valor )
   {
      System.err.println( Thread.currentThread().getName() +
         " escribe " + valor );

      bufer = valor;
   }
   // devolver valor de bufer
   public int obtener()
   {
      System.err.println( Thread.currentThread().getName() +
         " lee " + bufer );

      return bufer;
   }
} // fin de la clase BuferNoSincronizado

La clase Productor.java, que hereda de la clase Thread, posee un objeto llamado ubicacionCompartida que es la referencia al objeto que compartirá con Consumidor.java. Dentro del método run(), se itera 4 veces colocando un valor en el Bufer (usando el método establecer(int)), en lapsos de tiempo aleatorios de 0 a 3 segundos.

// El método run de Productor controla un subproceso que
// almacena los valores de 1 a 5 en ubicacionCompartida.
public class Productor extends Thread {
   private Bufer ubicacionCompartida; // referencia al objeto compartido
   // constructor
   public Productor( Bufer compartido )
   {
       super( "Productor" );
       ubicacionCompartida = compartido;
   }
   // almacenar valores de 1 a 4 en ubicacionCompartida
   public void run()
   {
      for ( int cuenta = 1; cuenta <= 4; cuenta++ ) {
         // estar inactivo de 0 a 3 segundos y luego colocar valor en Bufer
         try {
            Thread.sleep( ( int ) ( Math.random() * 3001 ) );
            ubicacionCompartida.establecer( cuenta );
         }
         // si se interrumpió el subproceso inactivo, imprimir rastreo de pila
         catch ( InterruptedException excepcion ) {
            excepcion.printStackTrace();
         }
      } // fin de instrucción for
      System.err.println( getName() + " termino de producir." +
         "\nTerminando " + getName() + ".");
   } // fin del método run
} // fin de la clase Productor

La clase Consumidor.java, que hereda de la clase Thread, posee un objeto llamado ubicacionCompartida que es la referencia al objeto que compartirá con Productor.java. Dentro del método run(), se itera 4 veces obteniendo el valor actual en el Bufer (usando el método obtener()), en lapsos de tiempo aleatorios de 0 a 3 segundos.

// El método run de Consumidor controla un subproceso que itera cuatro
// veces y lee un valor de ubicacionCompartida cada vez.
public class Consumidor extends Thread {
   private Bufer ubicacionCompartida; // referencia al objeto compartido
   // constructor
   public Consumidor( Bufer compartido )
   {
      super( "Consumidor" );
      ubicacionCompartida = compartido;
   }
   // leer el valor de ubicacionCompartida cuatro veces y sumar los valores
   public void run()
   {
      int suma = 0;
      for ( int cuenta = 1; cuenta <= 4; cuenta++ ) {
         // estar inactivo de 0 a 3 segundos, leer un valor de Bufer y agregarlo a suma
         try {
            Thread.sleep( ( int ) ( Math.random() * 3001 ) );
            suma += ubicacionCompartida.obtener();
         }
         // si se interrumpió el subproceso inactivo, imprimir rastreo de la pila
         catch ( InterruptedException excepcion ) {
            excepcion.printStackTrace();
         }
      }
      System.err.println( getName() + " leyo valores, dando un total de: " + suma +
         ".\nTerminando " + getName() + ".");
   } // fin del método run
} // fin de la clase Consumidor

Finalmente la clase PruebaBuferCompartido.java, que contiene el método main, crea un objeto compartido (llamado ubicacionCompartida) utilizado por los subprocesos. Posteriormente declara, incializa, y ejecuta dos subprocesos: Productor y Consumidor.

// PruebaBuferCompartido crea los subprocesos productor y consumidor.
public class PruebaBuferCompartido {
    public static void main( String [] args )
    {
        // crear el objeto compartido utilizado por los subprocesos
        Bufer ubicacionCompartida = new BuferNoSincronizado();
        // crear objetos productor y consumidor
        Productor productor = new Productor( ubicacionCompartida );
        Consumidor consumidor = new Consumidor( ubicacionCompartida );
        productor.start();  // iniciar subproceso productor
        consumidor.start();  // iniciar subproceso consumidor
    } // fin de main
} // fin de la clase PruebaBuferCompartido

El supuesto resultado debería ser que, después de que el Productor ponga un valor en el bufer, el Consumidor lea el valor. Pero, ya que los subprocesos no están sincronizados, al ejecutar la aplicación veremos algo lo siguiente:

#java PruebaBuferCompartido
Consumidor lee -1
Productor escribe 1
Productor escribe 2
Consumidor lee 2
Consumidor lee 2
Consumidor lee 2
Consumidor leyo valores, dando un total de: 5.
Terminando Consumidor.
Productor escribe 3
Productor escribe 4
Productor termino de producir.
Terminando Productor.

Como puedes ver, en esta ocación, el Consumidor intentó leer un dato antes de que el Productor. Además que, en ocaciones, el Productor escribe varias veces sobre el Bufer sin saber que el Consumidor se ha quedado, literalmente, dormido y no a obtenido el dato.

¿Cómo solucionar este problema de a-sincronismo?

La solución a este problema la trataremos en la siguiente entrada.

Descargar código fuente

Los ejercicios utilizados en este post están basados en ejemplos del libro Cómo programar en Java de Deitel, y por lo tanto están bajo la licencia que esta editorial disponga.

12 Comentarios | deja el tuyo

5 enlaces entrantes

7 Comentarios en “[código] Problema Productor-Consumidor: sincronización de Hilos en Java (1)”

  1. yrene dice:

    hola!, graciass por esta publicacion, la verdad ando investigando sobre sto ya qie me dejaron implementar el algoritmo lectores y escritores en java

  2. SOCIO dice:

    TEngo un problema tengo un codigo en c para compilarlo en linux y va todo bien pero tengo k psar este codigo a java y no hay manera si alguien me puede ayudar?

  3. briss dice:

    fdgtrhth
    hth

  4. Eugenio dice:

    Podrias Subirlo de nuevo tuve problemas para bajarlo ya no esta el link

  5. rbk dice:

    hola
    oye un megafavor curso el tercer semestre de ing.en sistemas computacionales me podrian decir donde puedo encontrar algunos codigos que corran en jcreator apartir de la unidad 4 en adelante topicos selectos de programacion.espero una respuesta gracias saludos bye..

  6. Suomi dice:

    No maaaamm… me acabas de salvar de una reva en la escuela…. xD

¡Déjanos tu comentario!