Programacin Orientada a Objetos Anexo 4 Threads Universidad
Programación Orientada a Objetos Anexo 4 Threads Universidad de Chile Departamento de Ciencias de la Computación Profesor: Juan Claudio Navarro jnavarro@dcc. uchile. cl, juancla. navarro@gmail. com
Temario n n n n Procesos y threads Creación de threads La clase Thread La interfaz Runnable Ciclo de vida de un thread Sincronización Métodos wait() y notify() Locks explícitos 2
Threads n n Usando clases y objetos es posible dividir un programa en secciones independientes A menudo es necesario además dividir un programa en tareas separadas que se ejecutan independientemente; estas tareas son los threads 3
Procesos y Threads n Procesos n n Un proceso es un programa ejecutándose Un proceso tiene: n n n un espacio de memoria asignada uno o más threads Threads n n Un thread es una tarea corriendo en una "CPU virtual" Los threads de un proceso comparten un mismo espacio de memoria 4
Procesos y Threads 5
¿Por qué usar Threads? n n n En ocasiones programas interactivos pueden lograr un mejor desempeño usando threads Para compartir el control al interior de un proceso Para hacer varias cosas "simultáneamente" Para que la interfaz de usuario pueda responder mientras se ejecuta otra tarea Para construir servidores que reciben conexiones simultáneas 6
Ejemplos n n n Un thread actualiza la pantalla mientras otro thread atiende el input del usuario Un servidor Web recibiendo múltiples conexiones simultáneamente Un programa simulador de carga que genera varios threads para estresar un servidor 7
Thread y Runnable n n n Dos maneras de crear un thread: Se crea una clase que extiende la clase Thread Se redefine (override) el método run() Se instancia la clase Se invoca el método start() sobre el objeto creado, para iniciar la ejecución del nuevo thread n n Se crea una clase que implementa la interfaz Runnable Se redefine (override) el método run() Se instancia un Thread entregando como parámetro una instancia Runnable Se invoca el método start() sobre el Thread creado, para iniciar la ejecución del nuevo thread 8
Un Ejemplo Sencillo public class Mi. Thread extends Thread { private int iteraciones = 5; private static int numero. Threads = 0; private int id. Thread = ++numero. Threads; public Mi. Thread() { System. out. println("Creando " + id. Thread); } public void run() { for (; iteraciones > 0; iteraciones--) { System. out. println("Thread " + id. Thread + " (" + iteraciones + ")"); } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new Mi. Thread(). start(); System. out. println("Todos los Threads iniciados"); } } 9
Manejo de Threads n Para crear un thread: n n 1. Crear e instanciar un objeto thread 2. Configurar el thread (opcional) 3. Iniciar el thread (start) Ejemplo: public class Report extends Thread { public void run() { n. Al invocar a start() la. . . JVM: ncrea un nuevo thread } ninvoca el método run() } del nuevo thread, dejando al thread activo class Cliente { n. El thread finaliza cuando el método run() retorna void f() { new Report(). start(); } } 10
La Interfaz Runnable n n En lugar de extender la clase Thread, es posible implementar la interfaz Runnable, y luego instanciar directamente un Thread Ejemplo: public class Report implements Runnable { public void run() {. . . } } class Cliente { void f() { Report report = new Report(); new Thread(). start(report); } } 11
Configuración de Threads n Prioridad (default: NORM_PRIORITY (5)) n n n Nombre n n set. Priority(int priority) El parámetro priority debe estar en el rango [Thread. MIN_PRIORITY (1), Thread. MAX_PRIORITY (10)] set. Name(String name) Demonio o usuario (default: usuario) n n set. Daemon(boolean on) Los threads demonio son proveedores de servicios para los threads de usuario La Virtual Machine detiene la ejecución del programa cuando no quedan threads de usuario vivos Para establecer un thread como demonio se debe invocar set. Daemon(true) antes de iniciar el thread 12
El Scheduler del JVM n n El Java Virtual Machine provee un scheduler que asigna tiempos de CPU a los diferentes threads del programa (priorizando a los threads de prioridades más altas) Cada JVM posee un thread activo (en ejecución), el que puede obtenerse invocando el método current. Thread(): System. out. print(Thread. current. Thread(). get. Name()); n Los demás threads pueden estar: n n listos para ejecutarse (runnable) bloqueados (blocked), por ejemplo esperando que se complete una operación de entrada / salida 13
Ciclo de Vida de un Thread n Un thread puede encontrarse en 4 estados: n n New: el thread ha sido creado pero aún no ha sido iniciado Runnable: el thread puede ser ejecutado por el scheduler Dead: la manera normal de morir de un thread es que su método run() finalice Blocked: el thread podría ejecutarse, pero algo lo impide; mientras se mantenga esta situación el scheduler no le entregará tiempo de CPU 14
Concurrencia Supongamos que los métodos set. Name y get. Name manejan un arreglo de caracteres (no un String) Memoria n JU CAROLINA Thread 1 Thread 2 Instrucción set. Name(“JUAN”) get. Name() Instrucción 15
Sincronización n n Para sincronizar la ejecución de diferentes threads sobre un mismo objeto se utiliza la palabra synchronized Los métodos y bloques synchronized se ejecutan secuencialmente sobre un objeto: mientras un método synchronized se ejecuta, no hay otros threads concurrentes ejecutando métodos o bloques synchronized sobre el mismo objeto public class Mi. Clase { public synchronized void set. Name(String name) {. . . } public synchronized void get. Name() {. . . } public void f() {. . . synchronized (this) {. . . } }. . . 16
El Monitor de Object y Class n n Java provee un “monitor” en la clase Object, utilizado para la sincronización de threads Cuando se invoca un método o un bloque synchronized sobre un objeto, su monitor es utilizado para bloquear el thread: n n Antes de ingresar al método o bloque sinchronized, el thread espera que el monitor del objeto esté disponible (quedando bloqueado si el monitor pertenece a otro thread) Una vez que el monitor se encuentra disponible, el thread ingresa al método o bloque synchronized, y pasa a ser el único dueño del monitor del objeto Al terminar el método o bloque synchronized, el thread pierde la propiedad del monitor Java provee un “monitor” en la clase Class, Class el cual es utilizado para sincronizar el acceso a métodos static 17 de la clase
Coordinación de Threads n n En ocasiones es necesario coordinar la ejecución de threads: llegado a un cierto punto de ejecución, un thread requiere que otro thread haya realizado una tarea particular para poder continuar Java provee los siguientes métodos en la clase Object n void wait([long timeout [, int nanos]]) n n void notify() n n Hace que el thread actual espere que algún thread invoque a notify() o a notify. All() sobre este objeto, o a que transcurra el tiempo indicado Despierta un thread que está esperando por este objeto void notify. All() n Despierta todos los threads que esperan por este objeto 18
Método wait() n void wait([long timeout [, int nanos]]) n n Hace que el thread actual espere que algún thread invoque a notify() o a notify. All() sobre este objeto, o a que transcurra el tiempo indicado El thread debe ser el dueño del monitor del objeto sobre el cual se invoca a wait() Al invocar a wait(), wait() el thread pierde la propiedad del monitor del objeto Al ser despertado por otro thread que invoca a notify() sobre el objeto, este thread espera hasta poder retomar la propiedad del monitor del objeto, y en cuanto lo consigue, retoma su ejecución 19
Métodos notify() y notify. All() n void notify() n n n Despierta un thread que está esperando por este objeto Si varios threads están esperando por este objeto, se escoge arbitrariamente uno de ellos para despertarlo El thread despertado competirá de la manera usual con otros threads por la propiedad del monitor del objeto, antes de retomar su ejecución El thread debe ser el dueño del monitor del objeto sobre el cual se invoca a notify() void notify. All() n Despierta todos los threads que esperan por este objeto (aparte de esto, es idéntico a notify()) notify() 20
Bloqueando un Thread n Un thread puede bloquearse por 5 motivos: n n n se ha invocado a sleep(milisegundos), sleep(milisegundos) el thread no se ejecutará por el tiempo indicado se ha invocado a wait(), wait() el thread no se ejecutará hasta que se invoque a notify() o notify. All() se ha invocado a suspend(), suspend() el thread no se ejecutará hasta que se invoque a resume() (*) el thread está esperando que se complete alguna entrada / salida el thread está intentando acceder a un método o una sección synchronized de un objeto cuyo monitor no se encuentra disponible (*) suspend() y resume() están “deprecated” 21
Locks Explícitos n n n Java 5 incorporó manejo de locks explícitos en el paquete java. util. concurrent. locks La interfaz Lock permite manejar locks de una manera similar al uso de synchronized, synchronized pero con la flexibilidad de controlar el fin del lock La clase Reentrant. Lock es una implementación de Lock equivalente al uso de synchronized Lock l = new Reentrant. Lock(); l. lock(); try { // acceso a recursos protegidos por este lock } finally { l. unlock(); } 22
Locks Explícitos n La interfaz Read. Write. Lock (implementada por la clase Reentrant. Read. Write. Lock) Reentrant. Read. Write. Lock provee dos locks asociados, uno para operaciones de lectura (compartido), y otro para operaciones de escritura (exclusivo) // ejemplo, sin manejo de excepciones por simplicidad Read. Write. Lock lock = new Reentrant. Read. Write. Lock(); // acceso compartido lock. read. Lock(). lock(); . . . lock. read. Lock(). unlock(); // acceso exclusivo lock. write. Lock(). lock(); . . . lock. write. Lock(). unlock(); 23
Otras Mejoras de Java 5 n Java 5 introdujo diversas mejoras en el manejo de threads (java. util. concurrent): java. util. concurrent n n La clase Thread. Pool. Executor permite ejecutar tareas utilizando un pool de threads La clase Blocking. Queue es una cola con operaciones que esperan si es necesario n void put(E o) n n E take() n n Agrega un elemento (si la cola está llena, espera que haya espacio disponible) Retorna y elimina el primer elemento (si la cola está vacía, espera a que haya un elemento) La clase Semaphore maneja una cantidad de permisos, y provee métodos acquire() y release() para adquirirlos y liberarlos 24
Resumen n n n Java permite utilizar threads para construir aplicaciones multitareas Para crear un thread, se puede extender la clase Thread, Thread o implementar la interfaz Runnable La lógica a realizar por el thread se programa en el método run() Cada objeto posee un monitor, definido en la clase Object Al invocar un método o bloque synchronized sobre un objeto, el thread pasa a ser el único propietario del monitor del objeto (bloqueándose si éste no se encuentra disponible) Un método static synchronized utiliza el monitor del objeto de tipo Class correspondiente a la clase 25
Resumen n Al invocar el método wait() sobre un objeto, el thread se bloquea a la espera de que otro thread invoque a notify() o notify. All() sobre el mismo objeto Al invocar al método wait(), wait() el thread libera el monitor del objeto, y lo recupera al ser notificado y proseguir su operación Java 5 incorporó locks explícitos, pools de locks, y otras funcionalidades adicionales 26
- Slides: 26