Este ejercicio introduce los conceptos básicos de hilos (threads) en Java, su ciclo de vida y cómo interactúan con la API REST que construimos en este proyecto.
- Comprender cómo crear y ejecutar hilos en Java.
- Observar la diferencia entre
start()yrun(). - Integrar la lógica de hilos en un nuevo endpoint REST y validarlo con pruebas unitarias.
src/
└── main/
└── java/co/eci/blacklist/
├── api/ # Controladores REST
├── application/ # Servicios de aplicación
├── domain/ # Lógica de negocio
├── infrastructure/ # Configuración y fachada
└── labs/part1/ # Ejercicios de hilos (Parte I)
└── labs/part2/ # Ejercicios de hilos (Parte II)
Cree una clase en co.eci.blacklist.labs.part1 llamada CountThread que extienda Thread.
Su método run() debe imprimir en consola todos los números dentro del rango [A..B].
public class CountThread extends Thread {
private final int from;
private final int to;
public CountThread(int from, int to) {
this.from = from;
this.to = to;
setName("CountThread-" + from + "-" + to);
}
@Override
public void run() {
for (int i = from; i <= to; i++) {
System.out.println("[" + getName() + "] " + i);
}
}
}Cree una clase principal que instancie y ejecute varios hilos usando la clase CountThread.
public class CountMainThreads {
public static void main(String[] args) throws InterruptedException {
CountThread t1 = new CountThread(0, 99);
CountThread t2 = new CountThread(99, 199);
CountThread t3 = new CountThread(200, 299);
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
}Cambie el incio con 'start()' por 'run()'. Cómo cambia la salida?, por qué?.
En un software de vigilancia automática de seguridad informática, se requiere validar direcciones IP en miles de listas negras conocidas y reportar aquellas que existan en al menos 5 de estas listas.
El componente se estructura así:
-
HostBlackListsDataSourceFacade
Fachada para consultar si una IP está en alguna de las N listas negras (isInBlacklistServer).
También permite reportar cuando una IP es considerada peligrosa o confiable.Esta clase no es modificable, pero es thread-safe.
-
BlacklistChecker
Clase del dominio que implementa el métodocheckHost(ip, nThreads).
Este método divide el espacio de servidores en segmentos y paraleliza la búsqueda usando hilos, siguiendo la política:- Si la IP aparece en ≥ 5 listas negras → se reporta como no confiable.
- En caso contrario → se reporta como confiable.
Además, retorna la lista de índices de listas negras donde se encontró la IP.
Cree una clase que represente el ciclo de vida de un hilo encargado de buscar en un segmento de servidores:
- Debe consultar el rango asignado de servidores con la fachada
HostBlackListsDataSourceFacade. - Guardar los índices de servidores donde aparece la IP.
- Permitir recuperar cuántas ocurrencias se encontraron.
En el proyecto actual esta lógica ya está encapsulada en BlacklistChecker, usando virtual threads.
Para efectos pedagógicos, puede implementarse una versión simplificada con
Threadclásico dentro del paquetelabs.part2.
En el método checkHost(String ip, int nThreads):
- Divida el total de servidores entre N segmentos.
- Recuerde manejar los casos donde N no divide de forma exacta.
- Cree N hilos (uno por segmento).
- Ejecute cada hilo con
start()y espere conjoin(). - Sume los resultados:
- Número total de servidores revisados.
- Número de coincidencias encontradas.
- Si las coincidencias son ≥ 5 (
BLACK_LIST_ALARM_COUNT), reporte la IP como no confiable; de lo contrario, como confiable. - Mantenga el LOG que informa antes de retornar:
INFO: Checked blacklists: X of Y INFO: HOST 205.24.34.55 Reported as trustworthy INFO: HOST 205.24.34.55 Reported as NOT trustworthy
En la API, este método ya existe en
BlacklistCheckery se expone a través deBlacklistControlleren el endpoint:GET /api/v1/blacklist/check?ip={ipv4}&threads={n}
200.24.34.55→ Está registrada varias veces en los primeros servidores (la búsqueda termina rápido).202.24.34.55→ Está registrada de forma dispersa, lo que hace más lenta la búsqueda.212.24.24.55→ No aparece en ninguna lista negra (peor caso).
Actualmente, aunque se encuentre la IP en ≥ 5 listas negras, los hilos siguen ejecutándose hasta terminar su segmento.
Pregunta para discusión:
¿Cómo se podría modificar la implementación para detener la búsqueda tempranamente cuando ya se alcanzó el umbral de 5 coincidencias?
- ¿Qué mecanismo de sincronización o bandera compartida sería necesario?
- ¿Qué implicaciones trae esto al control de la concurrencia?
Ahora que la búsqueda es paralela, evaluemos el impacto del número de hilos en el tiempo de ejecución.
Ejecute la validación de IP dispersa (202.24.34.55) con diferentes configuraciones de hilos:
- 1 hilo.
- Núcleos físicos del procesador (use
Runtime.getRuntime().availableProcessors()). - El doble de núcleos.
- 50 hilos.
- 100 hilos.
- Arranque la aplicación.
- Ejecute los experimentos uno a uno.
- Monitoree CPU y memoria en jVisualVM.

- Anote los tiempos de ejecución.
Con los datos recolectados, grafique:
- Eje X: número de hilos
- Eje Y: tiempo de ejecución (ms)
| Número de Hilos | Tiempo de Ejecución (ms) |
|---|---|
| 1 | 1200 |
| 4 | 400 |
| 8 | 250 |
| 16 | 220 |
| 50 | 210 |
| 100 | 215 |
Reemplace los valores con los obtenidos en sus experimentos.
Puede graficar los resultados usando Excel, Google Sheets o herramientas como plotly.
-
Según la Ley de Amdahl, ¿por qué el mejor desempeño no se logra con cientos de hilos (p. ej. 500)?

-
¿Qué ocurre al usar número de hilos = núcleos vs. el doble de núcleos?
-
¿Qué pasaría si, en lugar de un solo equipo, se distribuye el trabajo en 100 máquinas con un hilo cada una?
¿Mejoraría el rendimiento?
¿Cómo influye la fracción paralelizable (P) del problema?
- El problema de búsqueda en listas negras es un caso vergonzosamente paralelo (embarrassingly parallel).
- Aumentar hilos reduce el tiempo hasta cierto punto, pero hay un límite práctico por overhead.
- El desempeño óptimo suele alcanzarse con un número de hilos cercano al número de núcleos disponibles.
-
GET /api/v1/blacklist/check?ip={ipv4}&threads={n}
Respuesta:{ "ip": "200.24.34.55", "trustworthy": false, "matches": [0, 1, 2, 3, 4], "checkedServers": 123, "totalServers": 10000, "elapsedMs": 7, "threads": 8 }threads: si no se envía o es 0, usaavailableProcessors().- Umbral configurable (por defecto 5) en
application.yaml(blacklist.alarm-count).
-
Actuator:
/actuator/health,/actuator/prometheus.
mvn spring-boot:run
# o
mvn -DskipTests package && java -jar target/blacklist-api-0.0.1-SNAPSHOT.jardocker build -t blacklist-api:latest .
docker run --rm -p 8080:8080 blacklist-api:latest- Java 21 con Virtual Threads (executor por tarea) para paralelismo eficiente.
- Corte temprano al alcanzar el umbral (5 por defecto).
- Validación estricta de IPv4.
- Métricas por Actuator/Micrometer.
BlacklistCheckerTest: valida corte temprano y conteo de chequeos.BlacklistControllerTest: valida validación de IPv4 y flujo feliz.
MIT
