scjp, clase 9: threads

40
SCJP 6 Clase 9 – Threads Ezequiel Aranda Sun Microsystems Campus Ambassador

Upload: flekoso

Post on 05-Jul-2015

4.754 views

Category:

Technology


1 download

DESCRIPTION

Slides de la novena clase del curso de Java SCJP dictado en la Universidad Nacional de Centro de La Provincia de Buenos Aires.Contenido:1. Thread y Runnable2. Planificación3. Sincronización

TRANSCRIPT

Page 1: SCJP, Clase 9: Threads

SCJP 6 Clase 9 – Threads

Ezequiel Aranda

Sun Microsystems Campus Ambassador

Page 2: SCJP, Clase 9: Threads

Disclaimer & Acknowledgments

> Even though Ezequiel Aranda is a full-time employee of Sun Microsystems, the contents here are created as his own personal endeavor and thus does not reflect any official stance of Sun Microsystems.

> Sun Microsystems is not responsible for any inaccuracies in the contents.

> Acknowledgments – The slides of this presentation are made from “SCJP Unit 9” by Warit Wanwithu and Thanisa Kruawaisayawan and SCJP Workshop by P. Srikanth.

> This slides are Licensed under a Creative Commons Attribution – Noncommercial – Share Alike 3.0 > http://creativecommons.org/licenses/by-nc-sa/3.0/

Page 3: SCJP, Clase 9: Threads

AGENDA

> java.lang.Thread

> java.lang.Runnable

> Instanciación e inicialización de hilos

> Planificación

> Estados de los hilos

> Sincronización, locks y deadlocks

> Interacción entre hilos

Page 4: SCJP, Clase 9: Threads

Extendiendo java.lang.Thread

> Sobrescribir el método run(). Debería ser algo así: class MyThread extends Thread {

public void run() {

System.out.println("Important job running in MyThread"); } }

> La limitación de este enfoque es que al extender Thread, no podremos extender ninguna otra clase.

Page 5: SCJP, Clase 9: Threads

Extendiendo java.lang.Thread (II)

> Podemos también sobrecargar el método run(): class MyThread extends Thread {

public void run() {

System.out.println("Important job running in MyThread"); }

public void run(String s) {

System.out.println("String in run is " + s); } }

> Sin embargo, dicho método será ignorado por la clase Thread salvo que lo llamemos nosotros mismos y será ejecutado como un método normal.

Page 6: SCJP, Clase 9: Threads

Implementando java.lang.Runnable

> Implementando Runnable podremos, adicionalmente, extender de otra clase. class MyRunnable implements Runnable{

public void run() {

System.out.println("Important job running in MyRunnable");

}

Page 7: SCJP, Clase 9: Threads

Instanciando un Thread

> Si extendemos la clase Thread: MyThread t = new MyThread()

> Si implementamos la interfaz Runnable, también necesitaremos una instancia de Thread: MyRunnable r = new MyRunnable();

Thread t = new Thread(r);

> Suele pensarse en Thread como el “obrero” y Runnable como el “trabajo”.

Page 8: SCJP, Clase 9: Threads

Instaciando un thread (II)

> Existen varios constructores en la clase Thread: > Thread()

> Thread(Runnable target

> Thread(Runnable target, String name)

> Thread(String name)

Page 9: SCJP, Clase 9: Threads

Inicializando un Thread

t.start();

> ¿Y que pasa cuando llamamos a start()? > Se inicializa un nuevo hilo de ejecución (con su

stack de llamadas propio).

> El estado de dicho hilo pasa de “new” a “runnable”.

> Cuando se le de la posibilidad de ejecutarse, se ejecutará su método run().

Page 10: SCJP, Clase 9: Threads

Inicializando un Thread: Aclaraciones

> Llamar explicitamente a un método run() solo hará que se invoque al método run() del hilo que se encuentra en ejecución actualmente.

> El siguiente código no inicializa un nuevo hilo de ejecución (aunque es código legal): Runnable r = new Runnable();

r.run();

Page 11: SCJP, Clase 9: Threads

El planificador de threads

> El orden en el cual se ejecutará un conjunto de threads no está garantizado. class NameRunnable implements Runnable {

public void run() {

for (int x = 1; x <= 3; x++) {

System.out.println("Run by " + Thread.currentThread().getName() + ", x is " + x);

}

}

}

Page 12: SCJP, Clase 9: Threads

Estados de un Thread

> New: es el estado en el que se encuentra el hilo apenas después de su creación. En este punto no se considera que el hilo este “vivo”.

> Runnable: que un hilo se encuentre en este estado significa que es elegible para su ejecución, pero el planificador aún no lo ha seleccionado. En este punto se considera que el hilo esta vivo.

Page 13: SCJP, Clase 9: Threads

Estados de un Thread (II)

> Running: que un hilo esté en este estado significa que el planificador lo ha seleccionado para ser el hilo en ejecución.

> Waiting/ Blocked/ Sleeping: en cualquiera de estos estados, el hilo está vivo, pero no es elegible para ejecución. Puede volver al estado runnable en cualquier momento.

Page 14: SCJP, Clase 9: Threads

Estados de un Thread (III)

t.sleep() o t.yield()

> Se encuentran definidas para afectar al hilo que se encuentra en ejecución.

> Debemos recordar que un hilo bloqueado se considera vivo aún.

Page 15: SCJP, Clase 9: Threads

Estados de un Thread (IV)

> Dead: una vez que el hilo ha muerto, no puede revivirse. Invocar start() en un thread muerto resultará en una exception.

NEW

WAITING o

BLOCKED

RUNNABLE RUNNING DEAD

Page 16: SCJP, Clase 9: Threads

Sleeping

> sleep() es un método estático de la clase Thread.

> Se usa para frenar a un hilo forzandolo a ir al estado “sleeping” por una fracción de tiempo dada antes de volver a ser elegible para ejecución. try {

Thread.sleep(20*60*1000); //powernap

}

catch (InterruptedException ex) { }

Page 17: SCJP, Clase 9: Threads

Sleeping (II)

class NameRunnable implements Runnable { public void run() { for (int x = 1; x < 4; x++) { System.out.println("Run by "+ Thread.currentThread().getName());

try {Thread.sleep(1000); } catch (InterruptedException ex) { } } } }

> Sleep es una forma de dar a todos los hilos la posibilidad de ejecutarse.

Page 18: SCJP, Clase 9: Threads

yield()

> La idea de yield() es la de cambiar el estado del hilo en ejecución a runnable, dandole así la posibilidad de ejecutarse a otros hilos de la misma prioridad.

> Sin embargo, nada prohíbe al planificador volver a seleccionar para ejecución al mismo hilo una y otra vez.

Page 19: SCJP, Clase 9: Threads

join()

> Si un hilo B necesita que la tarea de un hilo A se complete para poder comenzar su ejecución, necesitamos hacer un “join” entre B y A.

> Esto hará que B no pueda pasar al estado runnable hasta que A complete su ejecución y pase al estado dead.

Page 20: SCJP, Clase 9: Threads

join() (II)

Page 21: SCJP, Clase 9: Threads

En resumen…

> sleep(): garantiza que el hilo en ejecución pase al estado “sleeping” por al menos el tiempo especificado.

> yield(): no garantiza absolutamente nada. Su función es la de volver al hilo en ejecución al estado “runnable”.

> join(): provoca que se detenga la ejecución del hilo actual hasta, por lo menos, la finalización del hilo con el que se realizó el join.

Page 22: SCJP, Clase 9: Threads

Sincronización

> Existe un problema conocido como “race condition” > Se da cuando múltiples hilos que pueden acceder a un

mismo recurso.

> Produce datos corruptos cuando los hilos acceden al valor de los datos entremedio de operaciones que deberían ser atómicas.

Page 23: SCJP, Clase 9: Threads

Sincronización(II)

> No hay forma de garantizar que un mismo hilo se mantendrá en ejecución durante toda la operación atómica.

> Pero sí es posible garantizar que incluso si el hilo no se mantiene en ejecución durante la operación atómica, ningún otro hilo pueda actuar sobre los mismos datos.

Page 24: SCJP, Clase 9: Threads

Sincronización (III)

> Entonces… ¿Cómo protegemos nuestros datos?

> Debemos hacer dos cosas > Hacer privadas a las variables

> Sincronizar el código que modifica las variables. (utilizando la palabra reservada synchronized).

> Ejemplo: private synchronized void makeWithdrawal(int amt)

Page 25: SCJP, Clase 9: Threads

Sincronización y locks

> Cada objeto en java tiene un lock, que se utiliza cuando el mismo tiene código marcado como synchronized en alguno de sus métodos.

> Cuando ingresamos en un método sincronizado, no estático, obtenemos automáticamente el lock de la instancia de la clase cuyo código estamos ejecutando.

Page 26: SCJP, Clase 9: Threads

Sincronización y locks (II)

> Dado que cada objeto solo tiene un lock, si un hilo ha obtenido dicho lock, ningún otro hilo podrá obtenerlo hasta que el primero lo libere.

> Si una clase tiene métodos sincronizados y métodos no sincronizados, múltiples hilos pueden aún acceder a los métodos no sincronizados.

> Cuando un hilo pasa a estar “dormido”, retiene todos sus locks.

Page 27: SCJP, Clase 9: Threads

Sincronización y locks (III)

> Se puede reducir el tamaño de las partes sincronizadas a un bloque (en vez de un método completo).

> En una muestra de originalidad propia del equipo de diseño de Stacy Malibú, esto se bautizó “bloque sincronizado”. class SyncTest {

public void doStuff() {

System.out.println("not synchronized");

synchronized(this) {

System.out.println("synchronized"); } } }

Page 28: SCJP, Clase 9: Threads

Sincronización y locks (IV)

public synchronized void doStuff() {

System.out.println("synchronized");

}

> Es equivalente a: public void doStuff() {

synchronized(this) {

System.out.println("synchronized");

}

}

Page 29: SCJP, Clase 9: Threads

Métodos estáticos sincronizados

> Sólo hay una copia de los datos estáticos, por lo que hará falta un lock para la clase completa para sincronizar métodos estáticos. public static int getCount() {

synchronized(MyClass.class) {

return count;

}

}

Page 30: SCJP, Clase 9: Threads

¿Qué sucede si un hilo no puede obtener un lock?

> Básicamente, el hilo va a parar a una especie de pool para dicho lock, en donde esperará a que se libere el lock y el hilo pueda volver al estado runnable.

> Al liberarse un lock, no hay forma de garantizar que un hilo en particular sea el que lo obtenga a continuación. > Ni siquiera hay forma de garantizar que el hilo

que espero por más tiempo sea el que lo obtenga.

Page 31: SCJP, Clase 9: Threads

¿Qué sucede si un hilo no puede obtener un lock? (II)

> Dos hilos que llaman a métodos sincronizados, no estáticos de la misma clase solo se bloquearan uno al otro si se invocan sobre la misma instancia. En caso contrario obtendrán un lock cada uno.

> En el caso de los métodos estáticos, los hilos siempre competirán por el mismo lock.

> Un método estático y uno no estático no se bloquearán uno al otro, nunca.

Page 32: SCJP, Clase 9: Threads

¿Qué sucede si un hilo no puede obtener un lock? (III)

> Para los bloques sincronizados, debemos fijarnos en cual fue el objeto utilizado en el cerrojo (dentro de los paréntesis que se encuentran luego de la palabra synchronized).

> Los hilos que utilizan el mismo objeto se bloquearán entre ellos. Los que utilizan distintos objetos, no.

Page 33: SCJP, Clase 9: Threads

Entonces, ¿cuándo necesito sincronizar?

> Simple: cuando un método que será usado por varios hilos accede a valores modificables.

> El acceso a campos estáticos debe ser hecho desde métodos estáticos sincronizados.

> El acceso a campos no estáticos sincronizados debe ser hecho desde métodos no estáticos sincronizados.

Page 34: SCJP, Clase 9: Threads

“Thread Safe”

> Cuando los métodos de una clase han sido cuidadosamente sincronizados para proteger los datos de la misma, llamamos a esa clase “Thread Safe”.

> Por ejemplo los métodos de StringBuffer se encuentran sincronizados, mientras que los de StringBuilder no lo están. Esto hace que en un ambiente multithread sea más seguro utilizar StringBuffer.

Page 35: SCJP, Clase 9: Threads

Deadlock

> Un deadlock ocurre cuando dos hilos se bloquean mutuamente esperando que el otro libere el lock que cada uno necesita.

> Ninguno puede continuar su ejecución hasta que el otro libere el lock por el que espera, por lo que se sientan a esperar para siempre…

Page 36: SCJP, Clase 9: Threads

Interacción entre hilos

> La clase Object tiene tres métodos: wait(), notify() y notifyAll(), que ayudan a los hilos a comunicar el estado de los eventos que les interesan.

> Un punto clave para el examen es recordar que cualquiera de estos métodos debe ser llamado desde un contexto sincronizado. Un hilo no puede invocar wait o notify en un objeto salvo que posea el lock de dicho objeto.

Page 37: SCJP, Clase 9: Threads

class ThreadA { public static void main(String [] args) { ThreadB b = new ThreadB(); b.start(); synchronized(b) { try { System.out.println("Waiting for b to complete...");

b.wait(); } catch (InterruptedException e) {} System.out.println("Total is: " + b.total);

} } }

Page 38: SCJP, Clase 9: Threads

class ThreadB extends Thread { int total; public void run() { synchronized(this) { for(int i=0;i<100;i++) { total += i; } notify(); } } }

Page 39: SCJP, Clase 9: Threads

Usando notifyAll() cuando varios hilos están esperando

> Podemos utilizar notifyAll() en el objeto para permitir que todos los hilos salgan del área de espera y vuelvan al estado runnable.

> De esta forma nos aseguramos que el thread correcto (junto con todos los otros) sea notificado.

Page 40: SCJP, Clase 9: Threads

Preguntas