- Threads in Java (Hasta 'Ending Threads')
- Threads vs Processes
Este ejercicio contiene una introducción a la programación con hilos en Java, además de la aplicación a un caso concreto.
Parte I - Introducción a Hilos en Java
-
De acuerdo con lo revisado en las lecturas, complete las clases CountThread, para que las mismas definan el ciclo de vida de un hilo que imprima por pantalla los números entre A y B.
-
Complete el método main de la clase CountMainThreads para que:
- Cree 3 hilos de tipo CountThread, asignándole al primero el intervalo [0..99], al segundo [99..199], y al tercero [200..299].
- Inicie los tres hilos con 'start()'.
- Ejecute y revise la salida por pantalla.
- Cambie el incio con 'start()' por 'run()'. Cómo cambia la salida?, por qué?.
Encontramos que los valores al iniciar con el método 'start()' ejecuta concurrentemente los hilos, lo que provoca que los valores mostrados en pantalla no sean secuenciales. Mientras que al ejecutarse con el método 'run()' si lo hace ya que empieza el siguiente hilo cuando termina el primero.
Parte II - Ejercicio Black List Search
Para un software de vigilancia automática de seguridad informática se está desarrollando un componente encargado de validar las direcciones IP en varios miles de listas negras (de host maliciosos) conocidas, y reportar aquellas que existan en al menos cinco de dichas listas.
Dicho componente está diseñado de acuerdo con el siguiente diagrama, donde:
-
HostBlackListsDataSourceFacade es una clase que ofrece una 'fachada' para realizar consultas en cualquiera de las N listas negras registradas (método 'isInBlacklistServer'), y que permite también hacer un reporte a una base de datos local de cuando una dirección IP se considera peligrosa. Esta clase NO ES MODIFICABLE, pero se sabe que es 'Thread-Safe'.
-
HostBlackListsValidator es una clase que ofrece el método 'checkHost', el cual, a través de la clase 'HostBlackListDataSourceFacade', valida en cada una de las listas negras un host determinado. En dicho método está considerada la política de que al encontrarse un HOST en al menos cinco listas negras, el mismo será registrado como 'no confiable', o como 'confiable' en caso contrario. Adicionalmente, retornará la lista de los números de las 'listas negras' en donde se encontró registrado el HOST.
Al usarse el módulo, la evidencia de que se hizo el registro como 'confiable' o 'no confiable' se dá por lo mensajes de LOGs:
INFO: HOST 205.24.34.55 Reported as trustworthy
INFO: HOST 205.24.34.55 Reported as NOT trustworthy
Al programa de prueba provisto (Main), le toma sólo algunos segundos análizar y reportar la dirección provista (200.24.34.55), ya que la misma está registrada más de cinco veces en los primeros servidores, por lo que no requiere recorrerlos todos. Sin embargo, hacer la búsqueda en casos donde NO hay reportes, o donde los mismos están dispersos en las miles de listas negras, toma bastante tiempo.
Éste, como cualquier método de búsqueda, puede verse como un problema vergonzosamente paralelo, ya que no existen dependencias entre una partición del problema y otra.
Para 'refactorizar' este código, y hacer que explote la capacidad multi-núcleo de la CPU del equipo, realice lo siguiente:
-
Cree una clase de tipo Thread que represente el ciclo de vida de un hilo que haga la búsqueda de un segmento del conjunto de servidores disponibles. Agregue a dicha clase un método que permita 'preguntarle' a las instancias del mismo (los hilos) cuantas ocurrencias de servidores maliciosos ha encontrado o encontró.
-
Agregue al método 'checkHost' un parámetro entero N, correspondiente al número de hilos entre los que se va a realizar la búsqueda (recuerde tener en cuenta si N es par o impar!). Modifique el código de este método para que divida el espacio de búsqueda entre las N partes indicadas, y paralelice la búsqueda a través de N hilos. Haga que dicha función espere hasta que los N hilos terminen de resolver su respectivo sub-problema, agregue las ocurrencias encontradas por cada hilo a la lista que retorna el método, y entonces calcule (sumando el total de ocurrencuas encontradas por cada hilo) si el número de ocurrencias es mayor o igual a BLACK_LIST_ALARM_COUNT. Si se da este caso, al final se DEBE reportar el host como confiable o no confiable, y mostrar el listado con los números de las listas negras respectivas. Para lograr este comportamiento de 'espera' revise el método join del API de concurrencia de Java. Tenga también en cuenta:
-
Dentro del método checkHost Se debe mantener el LOG que informa, antes de retornar el resultado, el número de listas negras revisadas VS. el número de listas negras total (línea 60). Se debe garantizar que dicha información sea verídica bajo el nuevo esquema de procesamiento en paralelo planteado.
-
Se sabe que el HOST 202.24.34.55 está reportado en listas negras de una forma más dispersa, y que el host 212.24.24.55 NO está en ninguna lista negra.
-
Parte II.I Para discutir la próxima clase (NO para implementar aún)
La estrategia de paralelismo antes implementada es ineficiente en ciertos casos, pues la búsqueda se sigue realizando aún cuando los N hilos (en su conjunto) ya hayan encontrado el número mínimo de ocurrencias requeridas para reportar al servidor como malicioso. Cómo se podría modificar la implementación para minimizar el número de consultas en estos casos?, qué elemento nuevo traería esto al problema?
Se podría usar una variable global, la cual fuera accesible para todos los hilos. Esta variable serviría como un contador de ocurrencias, cuando se registre el máximo, se terminaran todos hilos.
Parte III - Evaluación de Desempeño
A partir de lo anterior, implemente la siguiente secuencia de experimentos para realizar las validación de direcciones IP dispersas (por ejemplo 202.24.34.55), tomando los tiempos de ejecución de los mismos (asegúrese de hacerlos en la misma máquina):
- Un solo hilo.

- Tantos hilos como núcleos de procesamiento (haga que el programa determine esto haciendo uso del API Runtime).

- Tantos hilos como el doble de núcleos de procesamiento.

- 50 hilos.

- 100 hilos.
Al iniciar el programa ejecute el monitor jVisualVM, y a medida que corran las pruebas, revise y anote el consumo de CPU y de memoria en cada caso. 
Con lo anterior, y con los tiempos de ejecución dados, haga una gráfica de tiempo de solución vs. número de hilos. Analice y plantee hipótesis con su compañero para las siguientes preguntas (puede tener en cuenta lo reportado por jVisualVM):
En base a los resulsutados obtenidos, encontramos que a mayor cantidad de hilos, el tiempo de solución se reduce considerablemente.
Parte IV - Ejercicio Black List Search
-
Según la ley de Amdahls:
, donde S(n) es el mejoramiento teórico del desempeño, P la fracción paralelizable del algoritmo, y n el número de hilos, a mayor n, mayor debería ser dicha mejora. Por qué el mejor desempeño no se logra con los 500 hilos?, cómo se compara este desempeño cuando se usan 200?.
Al realizar pruebas con 500 (0,605 segundos) y 200 (1,019 segundos en ejecucón) hilos, de forma practica bajo las condiciones en las que fueron ejecutadas las pruebas encontramos que existe un mejor desempeño si se toma como n a 500. De forma teorica y usando la ley de Amdahls el mejor desempeño no se logra con 500 hilos dado a las caracteristicas del procesador del equipo en el que fueron ejecutadas las pruebas, el desempeño puede estar sujeto a otros factores del equipo, como otros programas ejecutando.
Si tomamos que la fracción del algoritmo que se puede hacer paralela es del 60% y al aplicar la fórmula de la ley de Amdahls encontramos los siguientes resultados: S(200)= 2.4813 y S(500)= 2.49 encontramos evidencia de que con 500 hilos efectivamente el tiempo de solución es mas corto, pero por la naturaleza de la función, esta tenderá al infinito y el rendimiento no continuara mejorando.
- Cómo se comporta la solución usando tantos hilos de procesamiento como núcleos comparado con el resultado de usar el doble de éste?.
Podemos apreciar que al usar el doble de núcleos, el tiempo de la solución es 4.2 veces más rapido. Además, el uso de memoria es el doble en el caso de usar tantos núcleos de procesamiento como tiene el computador.
- De acuerdo con lo anterior, si para este problema en lugar de 100 hilos en una sola CPU se pudiera usar 1 hilo en cada una de 100 máquinas hipotéticas, la ley de Amdahls se aplicaría mejor?. Si en lugar de esto se usaran c hilos en 100/c máquinas distribuidas (siendo c es el número de núcleos de dichas máquinas), se mejoraría?. Explique su respuesta.
En el primer caso, ejecutar el algoritmo en máquinas distintas podría traer un mejor rendimiento, dado que cada hilo podría aprovechar esta capacidad para resolver el subproblema, pero con los resultados de los experimentos podemos apreciar que un solo hilo para resolver el problema tiene un tiempo de desempeño muy alto. En el segundo caso podemos apreciar que la cantidad de máquinas depende de la cantidad de hilos, lo que mejoraría el rendimiento de la solución ya que estaría aprovechando los recursos de múltiples núcleos en cada máquina. Si utilizamos la definición teórica de la ley de Amdahls, ambos casos tendrían el mismo valor de la fracción paralelizable, si se ejecutan 100 hilos, el mejoramiento teórico seria el mismo.

