Producto-Consumidor Integrantes: Daimery Quintero González José Antonio Jimenes Corona Introducción: ¿Qué es el Algoritmo Producto Consumidor? El algoritmo de "producto-consumidor" es un patrón de programación concurrente o concurrencia utilizado para la comunicación y la sincronización entre procesos o hilos en un programa. Se usa comúnmente para resolver problemas relacionados con la gestión de recursos compartidos entre varios hilos o procesos. Análisis de Programa El escenario típico que ilustra este algoritmo involucra dos roles: el "productor" y el "consumidor". Los productores son responsables de generar datos o elementos, que luego se almacenan en una estructura de datos compartida, mientras que los consumidores retiran esos datos de la estructura compartida. La idea es que los productores y consumidores trabajen en paralelo de manera eficiente y sincronizada Clase Buffer: buffer[]: Un arreglo de caracteres que representa el buffer compartido. siguiente: Un índice que sigue el próximo lugar disponible en el buffer. estaLlena y estaVacia: Banderas que indican si el buffer está lleno o vacío, respectivamente. El constructor inicializa las variables y el arreglo. Método consumir(): Este método es sincronizado (synchronized), lo que significa que se puede acceder a él de manera segura desde múltiples hilos. Utiliza un bucle while para esperar si el buffer está vacío. Si está vacío, el hilo consumidor espera utilizando wait() hasta que un productor notifique que hay datos disponibles. Cuando el buffer no está vacío, se decrementa el índice siguiente, se recoge el valor en ese índice y se actualizan las banderas de estado (estaLlena y estaVacia). Finalmente, se notifica a todos los hilos para que puedan continuar o, en caso de que haya más consumidores esperando, uno de ellos pueda tomar el recurso. Método producir(char c): Este método también es sincronizado y opera de manera similar al método consumir(). Espera si el buffer está lleno y, una vez que hay espacio disponible, coloca el carácter c en el buffer, actualiza el índice y las banderas de estado, y notifica a otros hilos. Clase Productor: Extiende la clase Thread y representa un hilo productor. En su método run(), el productor genera caracteres aleatorios y llama al método producir() del buffer compartido para depositar esos caracteres. Luego, espera un tiempo aleatorio antes de continuar. Clase Consumidor: Al igual que la clase Productor, esta clase extiende Thread y representa un hilo consumidor. En su método run(), el consumidor llama al método consumir() del buffer compartido para recoger caracteres. También espera un tiempo aleatorio antes de continuar. Clase EjemploProductorConsumidor: El método main crea una instancia de la clase Buffer y dos hilos, uno para el productor y otro para el consumidor. Luego, inicia ambos hilos, lo que permite que la producción y el consumo de caracteres ocurran en paralelo. Código: //Daimery Quintero Gonzalez public class Buffer { private char buffer[]; private int siguiente; private boolean estaLlena; private boolean estaVacia; public Buffer(int tamanio) { buffer = new char[tamanio]; siguiente = 0; estaLlena = false; estaVacia = true; } public synchronized char consumir() { while (estaVacia) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } siguiente--; char valor = buffer[siguiente]; estaLlena = false; estaVacia = siguiente == 0; notifyAll(); return valor; } public synchronized void producir(char c) { while (estaLlena) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } buffer[siguiente] = c; siguiente++; estaLlena = siguiente == buffer.length; estaVacia = false; notifyAll(); } } 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) { char c = letras.charAt((int) (Math.random() * letras.length())); buffer.producir(c); System.out.println("Depositado el caracter " + c + " en el buffer"); try { sleep((int) (Math.random() * 4000)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } public class Consumidor extends Thread { private Buffer buffer; public Consumidor(Buffer buffer) { this.buffer = buffer; } public void run() { while (true) { char valor = buffer.consumir(); System.out.println("Recogido el caracter " + valor + " del buffer"); try { sleep((int) (Math.random() * 4000)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } public class EjemploProductorConsumidor { public static void main(String[] args) { Buffer b = new Buffer(10); Productor p = new Productor(b); Consumidor c = new Consumidor(b); p.start(); c.start(); } } Ejecución: Conclusión: En resumen, el programa ejemplifica la implementación del patrón de programación productor-consumidor en Java para gestionar la comunicación y la sincronización entre múltiples hilos en un entorno de concurrencia. Utiliza métodos sincronizados y estructuras de datos compartidas para asegurar un acceso seguro y coordinado a recursos compartidos. Esto ilustra la importancia de la sincronización en la programación concurrente y cómo abordar problemas de concurrencia de manera efectiva.