Hola a todos, hoy os voy a hablar sobre el modelo Productor Consumidor en Java.
Productor Consumidor es un modelo de comunicación entre hilos donde un hilo produce valores y otro hilo donde consume esos valores. Todo esto de manera sincronizada.
Ambos hilos tratan de manejar valores de una zona común que se le suele llamar Monitor (también se le suele llamar Buffer) que se encarga de sincronizar la entrada y salida de valores.
Sino recuerdas como funcionan los hilos, te dejo un tutorial aquí.
También es importante conocer el funcionamiento synchronized en Java, te dejo otro tutorial:
https://www.discoduroderoer.es/synchronized-en-java/
Veamos un ejemplo codificado de Productor Consumidor.
La clase Buffer se encarga de gestionar la entrada y salida de datos, con synchronized hacemos que solo entre un hilo a ejecutar la función cuando varios intentan entrar a la vez.
Cuando esta lleno, ejecutamos un wait para que el resto espere y cuando se consuma un valor, este notificará a todos con notifyAll que se puede producir de nuevo.
En este caso, la estructura de datos es un array de char pero puede ser aquello que necesites.
public class Buffer { private char buffer[]; private int siguiente; // Flags para saber el estado del buffer private boolean estaLlena; private boolean estaVacia; public Buffer(int tamanio) { buffer = new char[tamanio]; siguiente = 0; estaLlena = false; estaVacia = true; } // Método para retirar letras del buffer public synchronized char consumir() { // No se puede consumir si el buffer está vacío while (estaVacia == true) { try { wait(); // Se sale cuando estaVacia cambia a false } catch (InterruptedException e) { } } // Decrementa la cuenta, ya que va a consumir una letra siguiente--; // Comprueba si se retiró la última letra if (siguiente == 0) { estaVacia = true; } // El buffer no puede estar lleno, porque acabamos // de consumir estaLlena = false; notifyAll(); // Devuelve la letra al thread consumidor return (buffer[siguiente]); } // Método para añadir letras al buffer public synchronized void producir(char c) { // Espera hasta que haya sitio para otra letra while (estaLlena == true) { try { wait(); // Se sale cuando estaLlena cambia a false } catch (InterruptedException e) { } } // Añade una letra en el primer lugar disponible buffer[siguiente] = c; // Cambia al siguiente lugar disponible siguiente++; estaVacia = false; // Comprueba si el buffer está lleno if (siguiente == this.buffer.length) { estaLlena = true; } estaVacia = false; notifyAll(); } }
La clase Productor hereda de Thread para que sea un hilo, le pasamos el buffer como parámetro. El buffer es quien se encarga de añadir el valor producido.
public class Productor extends Thread { private Buffer buffer; private final String letras = "abcdefghijklmnopqrstuvxyz"; public Productor(Buffer buffer) { this.buffer = buffer; } public void run() { while (true) { // Obtenemos una letra al azar char c = letras.charAt((int) (Math.random() * letras.length())); // Producimos un valor buffer.producir(c); System.out.println("Depositado el caracter " + c + " del buffer"); try { // Esperamos entre 0 y 4 segundos sleep((int) (Math.random() * 4000)); } catch (InterruptedException e) { } } } }
La clase Consumidor hereda de Thread para que sea un hilo, le pasamos el buffer como parámetro. El buffer es quien se encarga de quitarlo del valor producido.
public class Consumidor extends Thread { private Buffer buffer; public Consumidor(Buffer buffer) { this.buffer = buffer; } public void run() { while (true) { // Consume el valor si es posible char valor = buffer.consumir(); System.out.println("Recogido el caracter " + valor + " del buffer"); try { // Esperamos entre 0 y 4 segundos sleep((int) (Math.random() * 4000)); } catch (InterruptedException e) { } } } }
public class EjemploProductorConsumidor { public static void main(String[] args) { Buffer b = new Buffer(10); Productor p = new Productor(b); Consumidor c = new Consumidor(b); try { p.start(); c.start(); } catch (InterruptedException ex) { System.out.println(ex.getMessage()); } } }
Este seria el resultado (puede variar):
Te dejo también un video, donde lo comento paso a paso.
Espero que os sea de ayuda. Si tenéis dudas, preguntad. Estamos para ayudarte.
mi caso es el siguiente, quiero ahcer un post a una WS desde otra WS a traves de una aplicacion Java, pero el POST solo me permite hacer 60 posts cada 40s, tengo un worker que carga los 60 productos y duerme los 40 s, mi pregunta es si a traves del Productor-Consumidor podría capturar esos 60 productos mientras el worker esta dormido. O hay otra manera de hacerlo?