Tema 6. Threads: programas multitarea Procesos e hilos Creación de threads Ciclo de vida de un thread Sincronización Prioridades Grupos de threads Relación de métodos Ejemplo sincronización 1 Procesos e hilos Programa multitarea: Es un programa en ejecución Posee su propia memoria Thread o hilo: Realización “simultánea” de 2 o más actividades Proceso: Proceso Flujo de ejecución dentro de un proceso Un proceso puede tener varios hilos en ejecución Threads 2 Creación de Threads Opción 1 Crear una clase derivada de la clase java.lang.Thread Sobrecargar el método run() Opción 2 Declarar una clase que implemente la interface java.lang.Runnable Definir el método run() Crear un objeto de tipo Thread pasándole al constructor el objeto de la nueva clase 3 Creación de un hilo derivando de la clase Thread class Hilo1 extends Thread { public Hilo1(String str) { super(str); } public void run() { for(int i=0;i<10;i++) System.out.println("Thread: " + getName()); } } public class TestHilo1 { public static void main(String arg[]) { Hilo1 miHilo = new Hilo1("Hilo de prueba"); miHilo.start(); } } 4 Creación de un hilo implementando la interface Runnable class Hilo2 implements Runnable { String nombreThread; public Hilo2(String str) { nombreThread = str; } public void run() { for(int i=0;i<10;i++) System.out.println("Thread: " + nombreThread); } } public class TestHilo2 { public static void main(String arg[]) { Hilo2 p = new Hilo2("Hilo de prueba"); Thread miHilo = new Thread(p); miHilo.start(); } } 5 Ciclo de vida de un Thread 6 Ejecución de un nuevo Thread La creación de un Thread no implica su ejecución Hay que inicializarlo con el método start() El método start() llama al método run() Después de ejecutar start(), el thread está en estado ejecutable (runnable) No quiere decir que se esté ejecutando en todo momento, ya que debe compartir la CPU con el resto de threads en estado runnable 7 Detención temporal (i) Los tiempos de CPU que el sistema asigna a los threads en estado runnable se emplean en ejecutar el método run() de cada thread Un thread puede renunciar a su tiempo de CPU y otorgárselo al sistema para asignárselo a otro thread → método yield() Si ningún thread requiere la CPU para una actividad intensiva, el sistema volverá asignar nuevo tiempo al thread generoso 8 Detención temporal (ii) Formas de parar un thread (not runnable): Ejecutando el método sleep() → detiene el thread el tiempo establecido. Suele llamarse desde el método run() Mediante el método wait() → detiene el thread hasta que se ejecuten los métodos notify() o notifyAll() Cuando el thread está esperando realizar operaciones de E/S Cuando el thread está intentando llamar a un método syncronized de un objeto, y el objeto está bloqueado por otro thread El thread pasa a estado runnable cuando cese alguna de las condiciones anteriores La clase Thread dispone de los métodos stop() y suspend(), pero su uso se desaconseja 9 Detención temporal (iii) Forma preferible de detener un thread: utilización conjunta de los métodos wait() y notifyAll() Modos de llamar a wait() 1) Indicando el tiempo máximo que debe estar parado Si se ejecuta notify() o notifyAll(), que indican la liberación de los objetos bloqueados, el thread continuará sin esperar a concluir el tiempo 2) Sin argumentos El thread permanece parado hasta reinicializarlo con los métodos notify() o notifyAll() Los métodos wait() y notify() deben estar incluidos en un método synchronized 10 Sincronización Se utiliza cuando: Los métodos que accedan a un recurso compartido deben declararse como synchronized 2 o más threads intentan acceder al mismo recurso al mismo tiempo Un thread debe esperar a que estén listos los datos que le debe suministrar otro thread Ej: public synchronized void metodoSincronizado() Si el método accede a un recurso, Java bloquea el recurso, de forma que el resto de threads no pueden acceder al mismo hasta que el primero finalice su tarea Bloquear un recurso u objeto: Sobre el objeto no pueden actuar 2 métodos sincronizados al mismo tiempo 11 Niveles de bloqueo A nivel de objetos Se declaran todos los métodos de la clase como synchronized Al ejecutar un método sobre un objeto, el sistema bloquea dicho objeto, permaneciendo a la espera cualquier thread que intente ejecutar algún método sincronizado del mismo objeto Si hay varios objetos, es posible tener distintos threads ejecutando métodos sobre diversos objetos A nivel de clases Se corresponde con los métodos de clase o static, y por tanto con las variables de clase o static 12 Prioridades Es posible establecer prioridades en los threads para repartir el tiempo de la CPU Las prioridades vienen definidas por variables miembro (de tipo entero) de la clase Thread. Constantes asociadas a prioridades: MAX_PRIORITY: máxima prioridad MIN_PRIORITY: mínima prioridad NORM_PRIORITY: prioridad por defecto Métodos: Para modificar la prioridad → método setPriority() Para obtener su prioridad → método getPriority() 13 Grupos de threads (i) Un hilo debe pertenecer a un grupo de hilos (ThreadGroup) Por defecto cuando se crea un thread pertenece al ThreadGroup del thread desde el que ha sido creado. Los grupos de threads permiten manejar múltiples threads como un solo objeto Si no se crea ningún ThreadGroup, pertenecerá al ThreadGroup llamado main, que se crea al arrancar el programa Para que un thread pertenezca a un grupo, hay que indicarlo al crearlo: Thread (ThreadGroup grupo, Runnable destino) Thread (ThreadGroup grupo, String nombre) Thread (ThreadGroup grupo, Runnable destino, String nombre) 14 Grupos de threads (ii) Un ThreadGroup debe pertenecer a otro ThreadGroup Si no se especifica ninguno, pertenecerá al ThreadGroup desde el que ha sido creado (por defecto el main) Constructores de ThreadGroup: ThreadGroup(ThreadGroup parent, String nombre); ThreadGroup(String name); → toma como parent el ThreadGroup al cual pertenece el thread desde el que se crea Ejemplo: ThreadGroup miThreadGroup = new ThreadGroup(“mi grupo”); Thread miThread = new Thread(miThreadGroup, “hilo de mi grupo”); 15 Relación de métodos (1) Clase Thread Métodos estáticos Thread currentThread(): Devuelve la tarea que se está ejecutando void yield(): Cede el control a otra tarea de la misma prioridad void sleep (long): Duerme la tarea los ms especificados Métodos de instancia void start(): Comienza la ejecución del Thread void run(): Cuerpo de la tarea void setPriority(int): Asigna la prioridad int getPriority(): Devuelve la prioridad void setName(String): Asigna un nombre String getName(): Devuelve el nombre 16 Relación de métodos (2) Clase Object Métodos para sincronización public final void wait() public final void notify() El thread actual espera hasta que otro thread utilice notify() o nofityAll() Despierta a un único thread que estuviera esperando Si hay varios, lo elige arbitrariamente public final void notifyAll() Despierta a todos los threads que estuvieran esperando 17 Ejemplo sincronización (1) HiloEscritor valor HiloLector Dato int obtener() void asignar(int) Funcionamiento: - El escritor escribe 5 datos - El lector lee 5 datos - Se debe leer cada dato escrito 18 Ejemplo sincronización (2) public class EjSincronizar { public static void main(String[] args) { Dato d = new Dato(); HiloEscritor esc=new HiloEscritor(d); HiloLector lec=new HiloLector(d); } } esc.start(); lec.start(); 19 Ejemplo sincronización (3) class HiloLector extends Thread { private Dato dato; public HiloLector(Dato d) { dato = d; } } public void run() { int valor; for(int i=0;i<5;i++){ valor = dato.obtener(); } } 20 Ejemplo sincronización (4) class HiloEscritor extends Thread { private Dato dato; public HiloEscritor(Dato d) { dato = d; } } public void run() { int valor; for(int i=0;i<5;i++){ dato.asignar(i); } } 21 Ejemplo sincronización (5) class Dato { private boolean disponible; private int valor; public Dato() { disponible = false; } public synchronized int obtener() { } } public synchronized void asignar(int v) { } 22 Ejemplo sincronización (6) public synchronized int obtener() { while(disponible==false){ try{ wait(); } catch(InterruptedException e){} } disponible = false; System.out.println("Leyendo: " +valor); notifyAll(); return valor; } 23 Ejemplo sincronización (7) public synchronized void asignar(int v) { while(disponible==true){ try{ wait(); } catch(InterruptedException e){} } valor = v; System.out.println("Escribiendo: " +valor); disponible = true; notifyAll(); } 24