Saltearse al contenido

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:

1
class 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á llena
13
}
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 hamburguesa
18
}
19
20
synchronized int servirHamburguesa() throws InterruptedException {
21
while (hamburguesas.isEmpty()) {
22
wait(); // El mesero espera si no hay hamburguesas
23
}
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 hamburguesas
27
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().

1
class 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ón
14
}
15
} catch (InterruptedException e) {
16
System.out.println("Chef interrumpido");
17
}
18
}
19
}
20
21
class 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 servicio
34
}
35
} catch (InterruptedException e) {
36
System.out.println("Mesero interrumpido");
37
}
38
}
39
}
40
41
class Restaurante {
42
public static void main(String args[]) {
43
Cocina cocina = new Cocina(5); // Máximo de 5 hamburguesas
44
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:

  1. Se usa ArrayList para almacenar las hamburguesas.
  2. Mantenemos el contador numHamburguesa para asignar un número único a cada hamburguesa.
  3. 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:

1
import java.util.ArrayList;
2
import java.util.List;
3
import java.util.Random;
4
5
class 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
34
class 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 variable
50
}
51
} catch (InterruptedException e) {
52
System.out.println("Chef " + id + " interrumpido");
53
}
54
}
55
}
56
57
class 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 variable
73
}
74
} catch (InterruptedException e) {
75
System.out.println("Mesero " + id + " interrumpido");
76
}
77
}
78
}
79
80
class RestauranteMultiple {
81
public static void main(String args[]) {
82
CocinaMultiple cocina = new CocinaMultiple(10); // Capacidad máxima de 10 hamburguesas
83
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:

  1. Seguimos usando una ArrayList estándar para las hamburguesas.
  2. Mantenemos un contador simple numHamburguesa para generar números únicos de hamburguesas.
  3. Los métodos prepararHamburguesa() y servirHamburguesa() están sincronizados para garantizar el acceso seguro a la lista compartida.
  4. Cada chef y mesero tiene un ID único para identificarlos.
  5. El método main() crea múltiples chefs y meseros, mostrando cómo manejar múltiples productores y consumidores.