Escuela Colombiana de Ingeniería – Arquitecturas de Software
Laboratorio de concurrencia: condiciones de carrera, sincronización, suspensión cooperativa y deadlocks, con interfaz Swing tipo Highlander Simulator.
- JDK 21 (Temurin recomendado)
- Maven 3.9+
- SO: Windows, macOS o Linux
Opción A (desde Main
, modo ui
)
mvn -q -DskipTests exec:java -Dmode=ui -Dcount=8 -Dfight=ordered -Dhealth=100 -Ddamage=10
Opción B (clase de la UI directamente)
mvn -q -DskipTests exec:java -Dexec.mainClass=edu.eci.arsw.highlandersim.ControlFrame -Dcount=8 -Dfight=ordered -Dhealth=100 -Ddamage=10
Parámetros
-Dcount=N
→ número de inmortales (por defecto 8)-Dfight=ordered|naive
→ estrategia de pelea (ordered
evita deadlocks,naive
los puede provocar)-Dhealth
,-Ddamage
→ salud inicial y daño por golpe
mvn -q -DskipTests exec:java -Dmode=demos -Ddemo=1 # 1 = Deadlock ingenuo
mvn -q -DskipTests exec:java -Dmode=demos -Ddemo=2 # 2 = Orden total (sin deadlock)
mvn -q -DskipTests exec:java -Dmode=demos -Ddemo=3 # 3 = tryLock + timeout (progreso)
- Start: inicia una simulación con los parámetros elegidos.
- Pause & Check: pausa todos los hilos y muestra salud por inmortal y suma total (invariante).
- Resume: reanuda la simulación.
- Stop: detiene ordenadamente.
Invariante: con N jugadores y salud inicial H, la suma total de salud debe permanecer constante (salvo durante un update en curso). Usa Pause & Check para validarlo.
edu.eci.arsw
├─ app/ # Bootstrap (Main): modes ui|immortals|demos
├─ highlandersim/ # UI Swing: ControlFrame (Start, Pause & Check, Resume, Stop)
├─ immortals/ # Dominio: Immortal, ImmortalManager, ScoreBoard
├─ concurrency/ # PauseController (Lock/Condition; paused(), awaitIfPaused())
├─ demos/ # DeadlockDemo, OrderedTransferDemo, TryLockTransferDemo
└─ core/ # BankAccount, TransferService (para demos teóricas)
- Ejecuta el programa de productor/consumidor y monitorea CPU con jVisualVM. ¿Por qué el consumo alto? ¿Qué clase lo causa?
- Ajusta la implementación para usar CPU eficientemente cuando el productor es lento y el consumidor es rápido. Valida de nuevo con VisualVM.
- Ahora productor rápido y consumidor lento con límite de stock (cola acotada): garantiza que el límite se respete sin espera activa y valida CPU con un stock pequeño.
Nota: la Parte I se realiza en el repositorio dedicado https://github.com/DECSIS-ECI/Lab_busy_wait_vs_wait_notify — clona ese repo y realiza los ejercicios allí; contiene el código de productor/consumidor, variantes con busy-wait y las soluciones usando wait()/notify(), además de instrucciones para ejecutar y validar con jVisualVM.
Usa monitores de Java:
synchronized
+wait()
+notify/notifyAll()
, evitando busy-wait.
Reescribe el buscador de listas negras para que la búsqueda se detenga tan pronto el conjunto de hilos detecte el número de ocurrencias que definen si el host es confiable o no (BLACK_LIST_ALARM_COUNT
). Debe:
- Finalizar anticipadamente (no recorrer servidores restantes) y retornar el resultado.
- Garantizar ausencia de condiciones de carrera sobre el contador compartido.
Puedes usar
AtomicInteger
o sincronización mínima sobre la región crítica del contador.
- Revisa la simulación: N inmortales; cada uno ataca a otro. El que ataca resta M al contrincante y suma M/2 a su propia vida.
- Invariante: con N y salud inicial
H
, la suma total debería permanecer constante (salvo durante un update). Calcula ese valor y úsalo para validar. - Ejecuta la UI y prueba “Pause & Check”. ¿Se cumple el invariante? Explica.
- Pausa correcta: asegura que todos los hilos queden pausados antes de leer/imprimir la salud; implementa Resume (ya disponible).
- Haz click repetido y valida consistencia. ¿Se mantiene el invariante?
- Regiones críticas: identifica y sincroniza las secciones de pelea para evitar carreras; si usas múltiples locks, anida con orden consistente:
synchronized (lockA) { synchronized (lockB) { // ... } }
- Si la app se detiene (posible deadlock), usa
jps
yjstack
para diagnosticar. - Aplica una estrategia para corregir el deadlock (p. ej., orden total por nombre/id, o
tryLock(timeout)
con reintentos y backoff). - Valida con N=100, 1000 o 10000 inmortales. Si falla el invariante, revisa la pausa y las regiones críticas.
- Remover inmortales muertos sin bloquear la simulación: analiza si crea una condición de carrera con muchos hilos y corrige sin sincronización global (colección concurrente o enfoque lock-free).
- Implementa completamente STOP (apagado ordenado).
- Código fuente (Java 21) con la UI funcionando.
Informe de laboratorio en formato pdf
con:- Parte I: diagnóstico de CPU y cambios para eliminar espera activa.
- Parte II: diseño de parada temprana y cómo evitas condiciones de carrera en el contador.
- Parte III:
- Regiones críticas y estrategia adoptada (orden total o tryLock+timeout).
- Evidencia de deadlock (si ocurrió) con
jstack
y corrección aplicada. - Validación del invariante con Pause & Check (distintos N).
- Estrategia para remover inmortales muertos sin sincronización global.
- Instrucciones de ejecución si cambias defaults.
- (3) Concurrencia correcta: sin data races; sincronización bien localizada; no hay espera activa.
- (2) Pausa/Reanudar: consistencia del estado e invariante bajo Pause & Check.
- (2) Robustez: corre con N alto; sin
ConcurrentModificationException
, sin deadlocks no gestionados. - (1.5) Calidad: arquitectura clara, nombres y comentarios; separación UI/lógica.
- (1.5) Documentación:
RESPUESTAS.txt
claro con evidencia (dumps/capturas) y justificación técnica.
- Estrategias de pelea:
-Dfight=naive
→ útil para reproducir carreras y deadlocks.-Dfight=ordered
→ evita deadlocks (orden total por nombre/id).
- Pausa cooperativa: usa
PauseController
(Lock/Condition), sinsuspend/resume/stop
. - Colecciones: evita estructuras no seguras; prefiere inmutabilidad o colecciones concurrentes.
- Diagnóstico:
jps
,jstack
, jVisualVM; revisa thread dumps cuando sospeches deadlock. - Virtual Threads: favorecen esperar con bloqueo (no busy-wait); usa timeouts.
mvn clean verify
Incluye compilación y pruebas JUnit.
Laboratorio basado en el enunciado histórico del curso (Highlander, Productor/Consumidor, Búsqueda distribuida), modernizado a Java 21.
Este contenido hace parte del curso Arquitecturas de Software (ECI) y está licenciado como Creative Commons Attribution-NonCommercial 4.0 International License.