Variantes de productor-consumidor
Introducción
El ejemplo anterior trabajaba únicamente con una hamburguesa a la vez. Sin embargo, en la vida real, es probable que el cocinero pueda preparar más de una hamburguesa antes de que el mesero las sirva. Aquí modificaremos el código para permitir que el cocinero prepare hasta un número máximo de hamburguesas que pueden mantenerse en una lista de tamaño fijo.
Comenzamos por la cocina, que ahora tendrá un buffer de hamburguesas:
1class Cocina {2 private final int capacidad;3 private final List<Integer> hamburguesas = new ArrayList<>();4 private int numHamburguesa = 0;5
6 public Cocina(int capacidad) {7 this.capacidad = capacidad;8 }9
10 synchronized void prepararHamburguesa() throws InterruptedException {11 while (hamburguesas.size() == capacidad) {12 wait(); // El chef espera si la cocina está llena13 }14 numHamburguesa++;15 hamburguesas.add(numHamburguesa);16 System.out.println("Chef preparó hamburguesa #" + numeroHamburguesa + ". Total en cocina: " + hamburguesas.size());17 notifyAll(); // Avisa a los meseros que hay una nueva hamburguesa18 }19
20 synchronized int servirHamburguesa() throws InterruptedException {21 while (hamburguesas.isEmpty()) {22 wait(); // El mesero espera si no hay hamburguesas23 }24 int hamburguesa = hamburguesas.remove(0);25 System.out.println("Mesero sirvió hamburguesa #" + hamburguesa + ". Quedan en cocina: " + hamburguesas.size());26 notifyAll(); // Avisa al chef que hay espacio para más hamburguesas27 return hamburguesa;28 }29}
El cocinero y el mesero se mantienen igual, pero ahora el cocinero prepara hamburguesas hasta que la cocina esté llena, esto se logra con la pausa wait()
y la notificación notifyAll()
.
1class Chef implements Runnable {2 Cocina cocina;3
4 Chef(Cocina cocina) {5 this.cocina = cocina;6 new Thread(this, "Chef").start();7 }8
9 public void run() {10 try {11 while(true) {12 cocina.prepararHamburguesa();13 Thread.sleep(50); // Simulamos el tiempo de preparación14 }15 } catch (InterruptedException e) {16 System.out.println("Chef interrumpido");17 }18 }19}20
21class Mesero implements Runnable {22 Cocina cocina;23
24 Mesero(Cocina cocina) {25 this.cocina = cocina;26 new Thread(this, "Mesero").start();27 }28
29 public void run() {30 try {31 while(true) {32 cocina.servirHamburguesa();33 Thread.sleep(100); // Simulamos el tiempo de servicio34 }35 } catch (InterruptedException e) {36 System.out.println("Mesero interrumpido");37 }38 }39}40
41class Restaurante {42 public static void main(String args[]) {43 Cocina cocina = new Cocina(5); // Máximo de 5 hamburguesas44 Chef chef = new Chef(cocina);45 Mesero mesero = new Mesero(cocina);46 System.out.println("Restaurante en funcionamiento");47 }48}
En esta implementación observamos los siguientes puntos:
- Se usa
ArrayList
para almacenar las hamburguesas. - Mantenemos el contador
numHamburguesa
para asignar un número único a cada hamburguesa. - El cocinero espera si la cocina está llena y notifica a los meseros cuando hay una nueva hamburguesa.
Ejemplo con múltiples cocineros y meseros
En la vida real, un restaurante puede tener más de un cocinero y mesero. Modificamos el código para tener múltiples cocineros y meseros:
1import java.util.ArrayList;2import java.util.List;3import java.util.Random;4
5class CocinaMultiple {6 private final int capacidad;7 private final List<Integer> hamburguesas = new ArrayList<>();8 private int numHamburguesa = 0;9
10 public CocinaMultiple(int capacidad) {11 this.capacidad = capacidad;12 }13
14 synchronized void prepararHamburguesa(int chefId) throws InterruptedException {15 while (hamburguesas.size() == capacidad) {16 wait();17 }18 numHamburguesa++;19 hamburguesas.add(numHamburguesa);20 System.out.println("Chef " + chefId + " preparó hamburguesa #" + numHamburguesa + ". Total en cocina: " + hamburguesas.size());21 notifyAll();22 }23
24 synchronized void servirHamburguesa(int meseroId) throws InterruptedException {25 while (hamburguesas.isEmpty()) {26 wait();27 }28 int hamburguesa = hamburguesas.remove(0);29 System.out.println("Mesero " + meseroId + " sirvió hamburguesa #" + hamburguesa + ". Quedan disponibles en cocina: " + hamburguesas.size());30 notifyAll();31 }32}33
34class ChefMultiple implements Runnable {35 CocinaMultiple cocina;36 int id;37 Random random = new Random();38
39 ChefMultiple(CocinaMultiple cocina, int id) {40 this.cocina = cocina;41 this.id = id;42 new Thread(this, "Chef-" + id).start();43 }44
45 public void run() {46 try {47 while(true) {48 cocina.prepararHamburguesa(id);49 Thread.sleep(50 + random.nextInt(100)); // Tiempo de preparación variable50 }51 } catch (InterruptedException e) {52 System.out.println("Chef " + id + " interrumpido");53 }54 }55}56
57class MeseroMultiple implements Runnable {58 CocinaMultiple cocina;59 int id;60 Random random = new Random();61
62 MeseroMultiple(CocinaMultiple cocina, int id) {63 this.cocina = cocina;64 this.id = id;65 new Thread(this, "Mesero-" + id).start();66 }67
68 public void run() {69 try {70 while(true) {71 cocina.servirHamburguesa(id);72 Thread.sleep(100 + random.nextInt(200)); // Tiempo de servicio variable73 }74 } catch (InterruptedException e) {75 System.out.println("Mesero " + id + " interrumpido");76 }77 }78}79
80class RestauranteMultiple {81 public static void main(String args[]) {82 CocinaMultiple cocina = new CocinaMultiple(10); // Capacidad máxima de 10 hamburguesas83 for (int i = 1; i <= 3; i++) {84 new ChefMultiple(cocina, i);85 }86 for (int i = 1; i <= 5; i++) {87 new MeseroMultiple(cocina, i);88 }89 System.out.println("Restaurante con múltiples chefs y meseros en funcionamiento. Presione Ctrl+C para detener.");90 }91}
En esta nueva versión vemos lo siguiente:
- Seguimos usando una
ArrayList
estándar para las hamburguesas. - Mantenemos un contador simple
numHamburguesa
para generar números únicos de hamburguesas. - Los métodos
prepararHamburguesa()
yservirHamburguesa()
están sincronizados para garantizar el acceso seguro a la lista compartida. - Cada chef y mesero tiene un ID único para identificarlos.
- El método
main()
crea múltiples chefs y meseros, mostrando cómo manejar múltiples productores y consumidores.