Uncontrolled Exceptions
Daemon Threads
Local Thread Variables
Count Down Latch
Programación Concurrente
2024
Ing. Ventre, Luis O.
Ing. Ludemann, Mauricio
Processing Uncontrolled Exceptions
in a Thread
Una Exception es un evento que interrumpe el flujo
normal de ejecución de un programa
Java clasifica las excepciones en dos tipos:
• Checked exceptions. FileNotFoundException,
IOException, SQLException, etc.
• Unchecked exceptions. NullPointerException,
ArrayIndexOutOfBoundsException,
IllegalArgumentException, etc.
Processing Uncontrolled Exceptions
in a Thread
• Checked exceptions:
• Son verificadas en tiempo de compilación, por lo tanto el
compilador de Java exige su manejo.
• Deben ser manejadas con bloque try/catch, o declaradas en
la firma del método con la palabra throws.
• Unchecked exceptions:
• No verificadas en tiempo de compilación, por lo tanto el
compilador no exige su manejo.
• El manejo debe ser implementado por el programador para
mejorar la robustez y el mantenimiento del sistema.
Processing Uncontrolled Exceptions
in a Thread
• Cuando una excepción checked es lanzada dentro
de un método run() de un objeto Thread,
debemos tratarla, ya que un método run() no
acepta clausula throws.
• Cuando una excepción unchecked es lanzada
dentro de un método run() de un objeto Thread, el
comportamiento por defecto es imprimir el
stackTrace en la consola y finalizar el programa.
– Afortunadamente, Java provee un mecanismo para
tratarlas y evitar la finalización abrupta del prog.
Processing Uncontrolled Exceptions
in a Thread
Se vera un ejemplo sobre como implementar el manejo
de excepciones unchecked.
Paso a Paso:
• Implementar la clase para tratar las excepciones
unchecked. Esta clase debe implementar la interface
UncaughtExceptionHandler, e implementar el método
uncaughtException() declarado en la interfaz.
• Dentro del método simplemente se puede hacer una
impresión por consola
Processing Uncontrolled Exceptions
in a Thread
Processing Uncontrolled Exceptions
in a Thread
Se fuerza la aparición de
una unchecked Exception
Processing Uncontrolled Exceptions
in a Thread
Processing Uncontrolled Exceptions
in a Thread
• Como Funciona?
– Cuando una excepción es lanzada en un hilo y es
unchecked, la JVM chequea si el hilo tiene un handler
de este tipo de excepciones. Si lo tiene el método
correspondiente es ejecutado.
– Existe otro metodo para el manejo de excepciones
setDefaultUncaughtExceptionHandler(), el cual aplica a
todos los hilos. Es un método estático.
– Si el hilo no tiene handler, la JVM imprime en la
consola el stackTrace y finaliza la ejecución.
Processing Uncontrolled Exceptions
in a Thread
• Cuando se produce una excepción unchecked en
un hilo, la JVM busca 3 posibles manejadores:
– Primero busca un handler del objeto Thread que lanzo
la excepción como se vio en el ejemplo.
– Si no existe, la JVM busca por un handler para un
grupo de hilos, que es más general que el anterior.
– Si tampoco existe, la JVM busca por el default método,
otro método para el manejo de excepciones
setDefaultUncaughtExceptionHandler(), el cual aplica a
todos los hilos.
– Si ninguno existe, imprime el stackTrace y finaliza.
Processing Uncontrolled Exceptions
in a Thread
La salida por consola:
Processing Uncontrolled Exceptions
in a Thread
• ¿Qué pasa si comentamos la línea 15?
Processing Uncontrolled Exceptions
in a Thread
• ¿Y si coloco ese try/catch?
Processing Uncontrolled Exceptions
in a Thread
• ¿Y ahora?
Creating and running a Daemon
Thread
• Java tiene un tipo especial de hilo “Daemon”:
– Este tipo de hilos tiene una muy baja prioridad de
ejecución. Normalmente ejecutan cuando ningún otro
hilo del mismo programa está ejecutando.
– Cuando los hilos daemons son solo los hilos vivos de
un programa la JVM los finaliza.
– Debido a estas características los hilos Daemons se
utilizan para brindar servicios en segundo plano.
– Generalmente no se utilizan para tareas importantes,
por la incertidumbre de cuando tendrán tiempo de
cpu.
Creating and running a Daemon
Thread
• En el siguiente ejercicio se verá como implementar
un hilo daemon.
• Objetivo:
– n hilos principales escriben eventos en una cola y el
hilo daemon será el encargado de ir limpiando los
eventos de la cola viejos (que tienen más de 10
segundos desde el tiempo de su creación).
Creating and running a Daemon
Thread
Creating and running a Daemon
Thread
Creating and running a Daemon
Thread
Setea Deamon en true
Creating and running a Daemon
Thread
Creating and running a Daemon
Thread
Creating and running a Daemon
Thread
Como funciona:
• Si analizamos una ejecución puede observarse
como la cola comienza a crecer hasta que alcanza
139 eventos. Su tamaño varia entre 126 y 139.
• El programa arranca con 14 hilos escritores. Cada
uno escribe un evento y se duerme por un
segundo.
• Luego de los primero 10 segundos, tenemos 140
eventos en la cola.
Creating and running a Daemon
Thread
• Luego de estos 10 segundos, el hilo daemon se
ejecutaba mientras los hilos escritores dormían,
pero no borro ninguno porque ninguno tenía mas
de 10 segundos de creado.
• Durante el resto de la ejecución el cleaner elimina
14 eventos por segundo y los 14 hilos crean 1 cada
uno. Por lo tanto la cola mantiene su tamaño.
• Puede jugar con el tiempo de creado de los
eventos para ser eliminados y el resultado?
Creating and running a Daemon
Thread
• Algunos detalles:
– El método setDaemon, solo puede ejecutarse ANTES
de iniciar el hilo. Una vez ejecutado start(), no puede
modificarse su daemon status.
– Se puede utilizar el método isDaemon(), para consultar
si el hilo es de este tipo. Este método devuelve true or
false.
Using Local Thread Variables
• Uno de los aspectos más críticos de las
aplicaciones concurrentes son las variables
compartidas.
• Esto tiene vital importancia en las clases que
extienden de Thread o implementan la interfaz
runnable.
Using Local Thread Variables
• Si se crea un objeto de una clase que implementa
la interfaz runnable, y luego crea varios hilos
usando el mismo objeto runnable como argument,
Todos los hilos comparten los mismo atributos del
objeto runnable!!!
• Si modifica un atributo en un hilo, afectará a
todos.
Using Local Thread Variables
• A veces se necesita un atributo que no sea
compartido por los hilos. Java provee un
mecanismo llamado variables locales de thread.
• Veremos el problema y la solución.
Using Local Thread Variables
Veremos un ejemplo del problema anterior:
• Crear una clase llamada UnsafeTask que
implemente runnable.
– Declarar un atributo privado de tipo date.
• Implemente el método run().
– Este método inicializa el atributo startDate, imprime su
valor por la consola, se duerme un número random de
tiempo y vuelve a escribirlo.
• Implemente la clase main. Este método debe crear
un objeto Unsafe Task y lanzar 3 hilos con igual
argumento. (durmiendo 2 segundo entre creación)
Using Local Thread Variables
Using Local Thread Variables
Using Local Thread Variables
• El resultado de la ejecución muestra cada hilo tiene
un tiempo de inicio diferente, pero el finalización
es corrupto.
Using Local Thread Variables
Local Thread Variable
• Crear una clase llamada SafeTask, que implemente
runnable.
• Declarar un objeto de la clase ThreadLocal<Date>.
– Este objeto tendrá un implementacion que incluye el
metodo InitialValue. Este método devuelve el valor
actual.
– Implemente el método run(). Funciona similar al
ejemplo anterior, solo cambia el acceso a la variable
local.
– Ejecute el ejemplo y COMPARE!
Using Local Thread Variables
Using Local Thread Variables
Using Local Thread Variables
• La ejecución del proyecto con variables locales de
hilo debería ser similar a:
Waiting for multiple concurrent events
• En java existe una clase llamada CountDownLatch,
que permite a uno o más hilos esperar, hasta que
un grupo de operaciones sea realizada.
– Esta clase se inicializa con un valor entero que
representa el número de acciones que el/los hilos van a
esperar para continuar.
– Cuando un hilo debe esperar la ejecución de éstas
acciones ejecuta el método await(). Este método lo
duerme hasta la completitud de todas las acciones.
Waiting for multiple concurrent events
• Cuando un hilo finaliza una acción ejecuta el método
countDown() para decrementar el contador interno.
• Cuando el contador llega a 0, la clase despierta a todos los
hilos dormidos en el método await() del
CountDownLatch.
• Se verá un ejemplo que modela un sistema de
videoconferencia.
– El sistema de videoconferencia deberá esperar el arribo de
todos los participantes antes de comenzar.
Waiting for multiple concurrent events
• Clase: VideoConference (Runnable)
– Declarar un objeto de la clase CountDownLatch
– Implementar el constructor. Inicializar el objeto de la
clase CountDownLatch, recordar que el sistema
esperará el arribo del número de participantes recibido
como parámetro.
– Implementar el método arrive(). Este método será
llamado cada vez que arribe un participante.
– Implementar el método run(). Primero se ejecuta
getCount(), para imprimir cantidad de participantes.
Luego llama el método await() para esperarlos. Y
finalmente imprime mensaje de arribo de todos.
Waiting for multiple concurrent events
Waiting for multiple concurrent events
Waiting for multiple concurrent events
Waiting for multiple concurrent events
• Ejecución
Waiting for multiple concurrent events
• La clase CountDownLatch tiene 3 elementos
básicos:
– El valor de inicialización que determina cuántos
eventos debe esperar.
– El método await(), llamado por los threads que quieren
esperar la finalización de todas las acciones.
– El método countDown(), llamado por los eventos
cuando finalizan la acción que sincronizará.
Waiting for multiple concurrent events
• Detalles a considerar:
– No hay ninguna forma de resetear el valor del contador
interno del countDownLatch. Ni modificarlo.
– Una vez inicializado en su valor, solo se puede
decrementar con el método countDown().
– Cuando el contador llega a 0, todas las llamadas al
método await() son devueltas inmediatamente y el
countDown() no tiene efecto.
Waiting for multiple concurrent events
• Detalles a considerar:
– Esta primitiva tiene diferencias con otras:
• No es utilizada para proteger un recurso compartido ni una sección
crítica.
• Es utilizado para sincronizar uno o más hilos con la ejecución de una
o más tareas.
• Solo admite un uso…se debe crear otro objeto si queremos hacer la
sincronización nuevamente.
– Existe una sobrecarga del método await() que puede recibir
hasta dos parámetros de tiempo (long, timeunit). El thread
se duerme hasta que llega a 0 o pasa ese tiempo y es
interrumpido.