Actividad 6: Simulación de una cocina con múltiples chefs (hilos)
En esta actividad, simularás una cocina con múltiples chefs (hilos) que preparan diferentes platos. Cada chef tendrá su propia tarea y compartirá recursos como la estufa y los utensilios de cocina. Deberás implementar la sincronización adecuada para evitar conflictos y garantizar que los chefs trabajen de manera eficiente.
Descripción de la Actividad
Se quiere simular una cocina donde trabajan varios chefs (hilos) preparando diferentes platos. Cada chef sigue una receta que consta de varios pasos (por ejemplo, 3 pasos). Cada paso tiene una duración aleatoria (entre 1 y 4 segundos) que se simula con Thread.sleep(). Los chefs deben imprimir mensajes indicando en qué paso se encuentran.
El chef principal (el hilo main) debe esperar a que todos los chefs terminen sus platos antes de servir la comida, usando join(). Además, existe un gerente (otro hilo) que, después de un tiempo, puede decidir interrumpir a un chef si considera que su plato se está demorando demasiado. Para ello, el gerente llama a interrupt() sobre el hilo del chef. Los chefs deben estar preparados para recibir esa interrupción y finalizar su trabajo de forma ordenada.
Requisitos
- Crear una clase Chef que extienda Thread o implemente Runnable. Cada chef tendrá un nombre y un número de pasos (por ejemplo, 3).
- En el método run():
- Iterar sobre cada paso.
- Antes de cada paso, verificar si el hilo ha sido interrumpido usando Thread.currentThread().isInterrupted(). Si es así, terminar el hilo mostrando un mensaje.
- Simular la duración del paso con Thread.sleep() y un tiempo aleatorio entre 1 y 4 segundos.
- Imprimir un mensaje indicando el paso actual y el tiempo que tomará.
- Manejar InterruptedException de manera que, si ocurre durante el sleep, se termine el hilo ordenadamente.
- En el método main:
- Crear un array de al menos 3 chefs (por ejemplo, "Juan", "María", "Carlos").
- Iniciar todos los chefs con start().
- Crear un hilo "gerente" que:
- Duerma durante 5 segundos (simulando el tiempo de espera del gerente).
- Luego elija un chef al azar que aún esté vivo (usando isAlive()) y llame a interrupt() sobre él.
- Después de lanzar al gerente, el main debe esperar a que todos los chefs terminen, usando join() para cada uno.
- Al final, imprimir un mensaje indicando que todos los chefs han finalizado (o que algunos fueron interrumpidos).
- Manejar adecuadamente las excepciones para que el programa no termine abruptamente.
Así mismo, una vez hecho lo anterior, realiza lo siguiente:
- Modificar el programa para que el gerente pueda interrumpir a más de un chef, por ejemplo, interrumpiendo a un chef cada 5 segundos durante un total de 15 segundos.
- Implementar que el chef, al ser interrumpido, pueda completar el paso actual antes de salir (verificando la interrupción solo entre pasos).
- Agregar un tiempo límite global: si pasados 10 segundos algún chef no ha terminado, el main interrumpe a todos y luego espera con join().
Código base de ejemplo
import java.util.Random;
public class Chef extends Thread {
private String nombre;
private int pasos;
private Random rand = new Random();
public Chef(String nombre, int pasos) {
this.nombre = nombre;
this.pasos = pasos;
}
@Override
public void run() {
IO.println("Chef " + nombre + ": Iniciando preparación (" + pasos + " pasos).");
try {
for (int i = 1; i <= pasos; i++) {
// Verificar interrupción antes de empezar el paso
if (Thread.currentThread().isInterrupted()) {
IO.println("Chef " + nombre + ": Me interrumpieron antes del paso " + i + ". Saliendo.");
return;
}
int tiempo = rand.nextInt(3000) + 1000; // 1 a 4 segundos
IO.println("Chef " + nombre + ": Paso " + i + "/" + pasos + " (" + tiempo/1000 + " segundos)");
Thread.sleep(tiempo);
}
IO.println("Chef " + nombre + ": ¡Plato terminado!");
} catch (InterruptedException e) {
IO.println("Chef " + nombre + ": Interrumpido durante un paso. Saliendo.");
// No es necesario restaurar la interrupción porque vamos a salir
}
}
}
public class Cocina {
void main() {
Chef[] chefs = {
new Chef("Juan", 3),
new Chef("María", 3),
new Chef("Carlos", 3)
};
for (Chef c : chefs) {
c.start();
}
// Hilo gerente
Thread gerente = new Thread(() -> {
try {
Thread.sleep(5000); // Espera 5 segundos antes de intervenir
// Buscar un chef que aún esté vivo
for (Chef c : chefs) {
if (c.isAlive()) {
System.out.println("Gerente: El chef " + c.getName() + " aún no termina. Lo interrumpo.");
c.interrupt();
break; // Solo interrumpimos a uno
}
}
} catch (InterruptedException e) {
System.err.println("Gerente interrumpido mientras esperaba.");
}
});
gerente.start();
// Esperar a que todos los chefs terminen
for (Chef c : chefs) {
try {
c.join();
} catch (InterruptedException e) {
System.err.println("Main interrumpido mientras esperaba a " + c.getName());
}
}
System.out.println("Main: Todos los chefs han finalizado. ¡A servir la comida!");
}
}
Explicación paso a paso del código base
- Clase Chef:
- Almacena el nombre y el número de pasos.
- En run(), itera sobre los pasos. Antes de cada paso comprueba si ha sido interrumpido; si es así, termina.
- Simula cada paso con Thread.sleep() y muestra mensajes.
- Captura InterruptedException para el caso de que la interrupción ocurra durante el sueño.
- Clase Cocina (main):
- Crea un array de chefs y los inicia.
- Crea un hilo gerente que espera 5 segundos y luego interrumpe al primer chef vivo que encuentre.
- El main hace join() sobre cada chef para asegurarse de que todos terminan antes de continuar.
- Finalmente imprime un mensaje.
Entrega
Para la entrega de la actividad deberán integrar en un único archivo PDF los siguientes elementos:
- Portada con el título de la actividad, nombre de los integrantes del equipo con sus respectivos números de control.
- Código fuente completo del programa, incluyendo las modificaciones adicionales.
- Capturas de pantalla que muestren la ejecución del programa, evidenciando la interrupción de los chefs y la finalización de todos los hilos.
- Reflexión contestando las siguientes preguntas:
- ¿Por qué es necesario verificar isInterrupted() antes de cada paso si ya manejamos InterruptedException?
- ¿Qué sucede si el gerente intenta interrumpir a un chef que ya terminó?
- ¿Cómo podríamos hacer para que el chef, al ser interrumpido, termine el paso actual antes de salir?
- ¿Qué ventajas tiene la cancelación cooperativa frente a una detención forzosa?
Recuerda que el código debe estar bien comentado y organizado para facilitar su comprensión. La reflexión debe ser clara y concisa, demostrando una comprensión profunda de los conceptos de concurrencia y manejo de hilos en Java.
Actividad 5: Explicando la Concurrencia a un Niño de 5 Años
Explicar conceptos de concurrencia de manera sencilla
Actividad 7: Sistema de retiro bancario
En esta actividad, simularás un sistema de retiro bancario donde múltiples clientes (hilos) intentan retirar dinero de una cuenta compartida. Deberás implementar la sincronización adecuada para evitar condiciones de carrera y garantizar que el saldo de la cuenta se actualice correctamente.