Vous êtes sur la page 1sur 21

Qu es un thread?

Hasta el momento hemos desarrollado programas


secuenciales con un nico thread: en cualquier
instante durante la ejecucin de un programa hay
un nico punto de ejecucin.

Java: Programacin Multithread


Franco Guidi Polanco

Un thread

Escuela de Ingeniera Industrial


Pontificia Universidad Catlica de Valparaso, Chile
fguidi@ucv.cl

Un programa

Franco Guidi Polanco

09-03-2007

Qu es un thread? (cont.)

Ejemplo

Un thread es un flujo secuencial de control dentro


de un programa
Un programa puede tener ms de un thread
ejecutndose al mismo tiempo
Dos
threads
Un programa

Una aplicacin que posee un thread que saluda y


otro que se despide:
Un programa

for(int i = 1; i<100;i++)

for(int i = 1; i<100;i++)

System.out.printl(Hola + i );

System.out.printl(Chao + i );

Dos threads
Franco Guidi Polanco

09-03-2007

Franco Guidi Polanco

09-03-2007

Ejemplo (cont.)

Creacin de mltiples threads en Java


Los threads en Java se implementan por
medio de la clase java.lang.Thread.

Output posible:
Hola
Hola
Chao
Hola
Chao
Chao
Hola

Franco Guidi Polanco

1
2
1
3
2
3
4

Existen dos formas de crear threads en


Java:
Extendiendo la clase java.lang.Thread
Implementando la interfaz
java.lang.Runnable

09-03-2007

Creacin de threads extendiendo la clase


java.lang.Thread

09-03-2007

09-03-2007

Creacin de threads extendiendo la clase


java.lang.Thread: ejemplo 1

El thread debe ser una clase que extiende la


clase Thread
Se debe sobreescribir el mtodo run() con
el cdigo que deber ser ejecutado por el
thread.
El thread debe ser lanzado invocando el
mtodo start() del objeto (heredado de la
clase Thread)

Franco Guidi Polanco

Franco Guidi Polanco

Dos ejemplos de clases que extienden Thread:


public class Hola extends Thread {
public void run() {
for (int i=1;i<100;i++)
System.out.println( "Hola" + i );
}
}
public class Chao extends Thread {
public void run() {
for (int i=1;i<100;i++)
System.out.println( "Chao" + i );
}
}

Franco Guidi Polanco

09-03-2007

Creacin de threads extendiendo la clase


java.lang.Thread: ejemplo 1 (cont.)

Qu ocurre en este ejemplo?

La aplicacin que lanza los threads:


...
public static void main(String arg[]) {

public class EjemploThreadSimple {


public static void main(String arg[]) {
Hola h = new Hola();
Chao c = new Chao();
h.start();
c.start();
System.out.println( Fin programa );
}
}

Hola h = new Hola();


Chao c = new Chao();

09-03-2007

c.start();
System.out.println( Fin programa );

Thread
Chao

c.start();

Thread
Hola

...

}
...

Franco Guidi Polanco

Estos dos
programas
funcionan
de igual
modo

09-03-2007

10

11

public class EjemploThreadSimple {


public static void main(String arg[]) {
Hola h = new Hola();
Chao c = new Chao();
h.start();
c.start();
System.out.println( Fin programa );
}
}
public class EjemploThreadSimple {
public static void main(String arg[]) {
new Hola().start();
new Chao().start();
System.out.println( Fin programa );
}
}

NOTA:
Ejecucin en JVM
sobre Windows XP.
El resultado puede ser
distinto en otras
plataformas
(se estudiar ms
adelante)
09-03-2007

h.start();

Si las referencias a los threads no son


necesarias...

Ejecucin del ejemplo 1

Franco Guidi Polanco

...

h.start();

Notar que para lanzar un nuevo thread se debe


invocar el mtodo start(). La simple invocacin
del mtodo run()produce slo su ejecucin en el
thread actual (como cualquier otro mtodo).
Franco Guidi Polanco

Thread
main

Franco Guidi Polanco

09-03-2007

12

Creacin de threads implementando la interfaz


java.lang.Runnable
La clase que contiene el proceso que ser lanzado
dentro de un nuevo thread debe implementar la
interfaz java.lang.Runnable
Particularmente se debe implementar en la clase
el mtodo run() declarado en Runnable, con el
cdigo que deber ser ejecutado por el thread
Para lanzar el thread se debe crear una instancia
de java.lang.Thread, pasando al constructor
de ste una referencia al objeto que implementa
Runnable, y luego se debe invocar el mtodo
start() del objeto Thread

Franco Guidi Polanco

09-03-2007

13

Creacin de threads implementando la interfaz


java.lang.Runnable: ejemplo 2
Dos ejemplos de clases que implementan Runnable:
public class Hola implements Runnable {
public void run() {
for (int i=1;i<100;i++)
System.out.println( "Hola" + i );
}
}
public class Chao implements Runnable {
public void run() {
for (int i=1;i<100;i++)
System.out.println( "Chao" + i );
}
}

Franco Guidi Polanco

09-03-2007

15

Creacin de threads implementando la interfaz


java.lang.Runnable (cont.)
Clase que implementa el cdigo del thread:
public class MiThread implements Runnable {
...
public void run() {
// cdigo del thread
}
...
}

Instanciacin y lanzamiento del thread:


...
Thread t = new Thread( new MiThread() );
t.start();
...
Franco Guidi Polanco

09-03-2007

14

Creacin de threads implementando la interfaz


java.lang.Runnable: ejemplo 2 (cont.)
La aplicacin que lanza los threads:
public class EjemploThreadSimple {
public static void main(String arg[]) {
Thread h = new Thread( new Hola() );
Thread c = new Thread( new Chao() );
h.start();
c.start();
System.out.println( Fin programa );
}
}

Notar que para lanzar un nuevo thread se debe crear una


instancia de Thread pasando al constructor una
referencia a un objeto Runnable, e invocar el mtodo
start() sobre Thread. La simple invocacin del mtodo
run() produce slo su ejecucin en el thread actual
(como cualquier otro mtodo).
Franco Guidi Polanco

09-03-2007

16

(Nuevamente) si las referencias a los


threads no son necesarias...
Estos dos
programas
funcionan
de igual
modo

Ejercicio

public class EjemploThreadSimple {


public static void main(String arg[]) {
Thread h = new Thread( new Hola() );
Thread c = new Thread( new Chao() );
h.start();
c.start();
System.out.println( Fin programa );
}
}
public class EjemploThreadSimple {
public static void main(String arg[]) {
new Thread( new Hola() ).start();
new Thread( new Chao() ).start();
System.out.println( Fin programa );
}
}

Franco Guidi Polanco

09-03-2007

17

Cree un programa similar al ejemplo anterior que,


en vez de usar clases distintas para manejar los
threads correspondientes a los diferentes
mensajes (Hola, Chao), utilice dos thread de una
misma clase (cuyo mensaje sea configurable).
Haga una implementacin del thread extendiendo
la clase Thread y otra implementando Runnable.
Haga la aplicacin que lance los threads.

Franco Guidi Polanco

Solucin

09-03-2007

Solucin (cont.)

Versin que extiende Thread:

Versin que implementa Runnable:

public class Habla extends Thread {


String mensaje;
public Habla(String msg){
mensaje = msg;
}
public void run() {
for (int i=1;i<100;i++)
System.out.println( mensaje + i );
}
}

public class Habla implements Runnable {


String mensaje;
public Habla(String msg){
mensaje = msg;
}
public void run() {
for (int i=1;i<100;i++)
System.out.println( mensaje + i );
}
}

public class Ejercicio {


public static void main(String arg[]) {
Habla h = new Habla( Hola ) ;
Habla c = new Habla( Chao );
h.start();
c.start();
}
}

public class Ejercicio {


public static void main(String arg[]) {
Thread h = new Thread( new Habla( Hola ) ) ;
Thread c = new Thread( new Habla( Chao ) );
h.start();
c.start();
}
}

Franco Guidi Polanco

09-03-2007

18

19

Franco Guidi Polanco

09-03-2007

20

Mtodos tiles de la clase Thread: sleep


Detiene la ejecucin del thread actual por la
cantidad de milisegundos indicada como parmetro.
public static void sleep(long millis) throws InterruptedException

Ejemplo:
Equivalente:
public class MyThread extends Thread {
...
public void run() {
...
try{
sleep( 1000 );
}catch(InterruptedException e){
//Interrupcin
}
...
}
}
Franco Guidi Polanco

09-03-2007

Thread.sleep( 1000 );

NOTA: Si en vez de
extender Thread se
hubiese implementado
Runnable, entonces aqu
hubiese sido necesaria la
referencia a la clase
Thread para invocar el
mtodo sleep().
21

Mtodos tiles de la clase Thread: sleep


Ejemplo

Mtodos tiles de la clase Thread: sleep


Ejemplo
public class Hola extends Thread {
public void run() {
for (int i=1;i<10;i++){
System.out.println( "Hola" + i );
try{
Thread.sleep( 1000 );
}catch(InterruptedException e){}
}
}
}
public class Chao extends Thread {
public void run() {
for (int i=1;i<10;i++){
System.out.println( "Chao" + i );
try{
sleep( (long) ( Math.random() * 1000) );
}catch (InterruptedException e){}
}
}
}
Franco Guidi Polanco

Detencin de 1000 ms
(1 segundo)

Tiempo de detencin
al azar

09-03-2007

22

Mtodos tiles de la clase Thread: yield

Detiene la ejecucin del thread actual y


permite a otros threads ser ejecutados
public static void yield()

Ejemplo:
Equivalente:
public class MyThread extends Thread {
...
public void run() {
...
yield();
...
}
}

Franco Guidi Polanco

09-03-2007

23

Franco Guidi Polanco

09-03-2007

Thread.yield();

NOTA: Si en vez de
extender Thread se
hubiese implementado
Runnable, entonces aqu
hubiese sido necesaria la
referencia a la clase
Thread para invocar el
mtodo yield().
24

Mtodos tiles de la clase Thread: yield


(Ejemplo)

Mtodos tiles de la clase Thread: yield


(Ejemplo)

public class Hola extends Thread {


public void run() {
for (int i=1;i<10;i++){
System.out.println( "Hola" + i );
Thread.yield();
}
}
}

public class Chao extends Thread {


public void run() {
for (int i=1;i<10;i++){
System.out.println( "Chao" + i );
yield();
}
}
}

Franco Guidi Polanco

09-03-2007

25

Ciclo de vida de un thread

Ejecutndose
(Running)
yield
Nuevo thread
(New Thread)

start

Ejecutable
(Runnable)

Franco Guidi Polanco

09-03-2007

26

Ciclo de vida de un thread (cont.)


Inicio de un thread - el thread se ha creado
(Nuevo thread) y se invoca el mtodo start. El
thread pasa al estado ejecutable.
Transicin del estado ejecutable al estado no
ejecutable- ocurre por uno de estos tres motivos:

sleep
wait
bloqueado en I/O
No ejecutable
(Not Runnable)

El thread invoca el mtodo sleep


El thread invoca el mtodo wait (*)
El thread se bloquea en una operacin de I/O

finaliza el mtodo run


Muerto
(Dead)

Franco Guidi Polanco

09-03-2007

Tomado de
The Java Tutorial
Sun Microsystems

27

(*) ser estudiado ms adelante

Franco Guidi Polanco

09-03-2007

28

Ciclo de vida de un thread (cont.)

Ciclo de vida de un thread (cont.)

Transicin del estado no ejecutable a


ejecutable:
si el thread haba invocado el mtodo sleep, el nmero
de milisegundos de pausa ha transcurrido
si el thread haba invocado el mtodo wait de un
objeto, otro thread lo notifica de continuar por llamando
al mtodo notify o al mtodo notifyAll del mismo
objeto (*)
si el thread estaba bloqueado en operacin de I/O, la
operacin se ha completado
(*) sern estudiados ms adelante
Franco Guidi Polanco

09-03-2007

29

Transicin del estado ejecutable al estado


muerto: ocurre cuando el mtodo run llega a su
fin.
El mtodo isAlive ayuda a conocer el estado de
un thread:
Retorna true si el thread ha sido lanzado (mtodo
start invocado) y no detenido
Retorna false si el thread est en el estado Nuevo
thread (no ha sido lanzado) o est muerto (mtodo
run terminado)
Franco Guidi Polanco

Ejecucin de threads
En un sistema con mltiples CPU, cada CPU podra
ejecutar un thread distinto

Concurrencia
Si no es posible el paralelismo, una CPU es responsable
de ejecutar mltiples threads
Thread A

Thread B

Thread A

Thread A

Thread B

Thread B

Thread A

Thread B

Thread A

...

...

...

CPU

CPU

CPU

09-03-2007

30

Prioridades de los threads

Paralelismo

Franco Guidi Polanco

09-03-2007

31

La ejecucin de mltiples threads en una


sola CPU requiere la determinacin de una
secuencia de ejecucin (scheduling)
Java soporta un algoritmo de secuenciacin
de threads simple denominado fixed priority
scheduling
Este algoritmo secuencia la ejecucin de
threads en base a la prioridad relativa que
les ha sido asignada

Franco Guidi Polanco

09-03-2007

32

Prioridades de los threads (cont.)

El fixed priority scheduling de Java

Cuando se crea un nuevo thread, su prioridad


relativa es la misma que la del thread que lo cre
La prioridad de un thread puede ser cambiada en
cualquier momento por medio del mtodo
setPriority. Este mtodo recibe un entero que
indica el valor de prioridad (valores ms altos
indican ms altas prioridades)
La clase Thread declara tres constantes:
public static final int MAX_PRIORITY
public static final int MIN_PRIORITY
public static final int NORM_PRIORITY

Franco Guidi Polanco

10
1
5

09-03-2007

33

Threads y la portabilidad de Java:


debilidades

09-03-2007

34

La cosa puede ser peor aun:

... distintos sistemas operativos manejan los


threads en distinta forma: por ejemplo NT y
Solaris tienen diferentes niveles de prioridades
(incluso diferentes respecto los que define Java)

09-03-2007

Franco Guidi Polanco

Threads y la portabilidad de Java:


debilidades (cont.)

La responsabilidad de ejecucin de los


threads es pasada al sistema operativo
Pero...

Franco Guidi Polanco

Entre todos los threads en estado ejecutable es


escogido el thread con la prioridad ms alta
Si hay dos threads con la misma prioridad, es
escogido uno de ellos en modo round-robin
Cuando el thread en ejecucin pasa al estado no
ejecutable o muerto otro thread es seleccionado
para su ejecucin.
La ejecucin de un thread es interrumpida si otro
thread con ms alta prioridad se vuelve
ejecutable.

35

existen sistemas operativos que implementan


time slicing (subdivisin de tiempo): el sistema
operativo asigna una porcin de tiempo a la
ejecucin de cada thread. En este caso la
ejecucin de un thread es interrumpida no slo
si otro thread con ms alta prioridad se vuelve
ejecutable, sino tambin cuando su tiempo
asignado de ejecucin se acaba.
(no todos los sistemas operativos implementan
time slicing)

Franco Guidi Polanco

09-03-2007

36

Threads egostas

Threads egostas (cont.)

Si un sistema operativo no implementa time slicing, y si el


thread no sale del estado ejecutable, este continuar su
ejecucin hasta que muera. Mientras tanto, ningn otro
thread podr ser ejecutado.
Volvamos el ejemplo inicial:
public class Habla extends Thread {
String mensaje;
public Habla(String msg){
mensaje = msg;
}
public void run() {
for (int i=1;i<100;i++)
System.out.println( mensaje + i );
}
}

Por lo tanto el siguiente programa ejecutar:


ambos threads contemporneamente en un sistema con
time slicing (vase su ejecucin en ejemplos anteriores)
slo el thread que imprime Hola hasta terminar las 99
impresiones, y luego el thread que imprime Chao, en un
sistema que no soporta time slicing
public class Ejercicio {
public static void main(String arg[]) {
Habla h = new Habla( Hola ) ;
Habla c = new Habla( Chao );
h.start();
c.start();
}
}

En un sistema sin time slicing, este se vuelve un thread egosta.


Franco Guidi Polanco

09-03-2007

37

Franco Guidi Polanco

Moraleja sobre el egosmo y los threads


No se debe asumir que la ejecucin de una aplicacin se
har siempre en un sistema que soporta time slicing.
Por lo tanto, se debe incluir adecuadamente
invocaciones a los mtodos yield, sleep y wait, si los
threads no se bloquean en operaciones de I/O.
public class Habla extends Thread {
String mensaje;
public Habla(String msg){
mensaje = msg;
}
public void run() {
for (int i=1;i<100;i++){
System.out.println( mensaje + i );
yield();
}
}
}
Franco Guidi Polanco

09-03-2007

09-03-2007

38

Acceso a datos compartidos


Es comn que dos o ms threads tengan acceso a
objetos en comn
Ejemplo
Supongamos una aplicacin con dos threads que
actualizan un objeto compartido, de la clase
Historial:
public class Historial {
String[] mensajes = new String[1000];
int pos = 0;
public void agregar(String msg) {
mensaje[pos] = msg;
pos++;
}
}

39

Franco Guidi Polanco

09-03-2007

40

Acceso a datos compartidos (cont.)

Acceso a datos compartidos (cont.)


Se espera que ocurra lo siguiente:

public class Habla extends Thread {


String mensaje;
Historial historial;
public Habla(String msg, Historial h){
mensaje = msg;
historial = h;
}
public void run() {
for (int i=1;i<100;i++){
historial.agregar( mensaje );
yield();
}
}

Thread Hola
mensaje[pos]=msg;
pos++;

mensaje[pos]=msg;
pos++;

41

Acceso a datos compartidos (cont.)

Thread Chao
mensaje[0]=Hola
mensaje[pos]=msg;
pos++;

pos++;

pos = 2

mensaje[pos]=msg;
pos++;

mensaje[2]=Hola
pos = 3

mensaje[pos]=msg;

pos++;

Franco Guidi Polanco

mensaje[3]=Chao
pos = 4

09-03-2007

42

Bloqueo del objetos compartidos

Pero podra ocurrir lo siguiente:

mensaje[pos]=msg;

Franco Guidi Polanco

mensaje[1]=Chao
pos = 2

mensaje[2]=Hola
pos = 3

mensaje[pos]=msg;
pos++;

09-03-2007

Thread Hola

mensaje[0]=Hola
pos = 1
mensaje[pos]=msg;
pos++;

public class Ejercicio {


public static void main(String arg[]) {
Historial historial = new Historial();
Habla h = new Habla( Hola, historial ) ;
Habla c = new Habla( Chao, historial );
h.start();
c.start();
}
}
Franco Guidi Polanco

Thread Chao

mensaje[0]=Chao
pos = 1

mensaje[pos]=msg;

mensaje[3]=Chao

pos++;

pos = 4

mensaje[3]=Hola

La sincronizacin para el acceso a objetos compartidos se


basa en el concepto de monitor, desarrollado por C.A.R.
Hoare.
Un monitor es una porcin de cdigo protegida por un
mutex (mutual exclusion semaphore).
Slo un thread puede tener el mutex de un objeto en un
momento dado.
Si un segundo thread trata de obtener un mutex ya
adquirido por otro thread, se bloquea hasta que el primero
libere el mutex.
Al momento de liberarse un mutex, todos los threads en
espera de l se despertarn; en base a algn criterio
(orden de prioridad, FIFO, etc.) el mutex ser dado a uno
de ellos.

pos = 5

09-03-2007

43

Franco Guidi Polanco

09-03-2007

44

Bloqueo del objetos compartidos: mtodos


synchronized

Bloqueo del objetos compartidos: analoga


Un edificio en el cual algunas oficinas tienen llave y otras tiene libre
acceso. El monitor es el conjunto de oficinas cuyo acceso requiere la
llave.
Los threads son las personas que quieren acceder a las oficinas.
Para entrar a una oficina con llave, una persona tiene que obtener el
manojo con las llaves de la oficina. El manojo con las llaves es el
mutex del edificio: solo la persona que tiene el manojo puede ingresar
a las oficinas con llave. Las otras son de libre acceso.

En Java el bloqueo de un objeto ocurre cuando un thread


entra a un mtodo declarado como synchronized (de un
objeto compartido).
public class Historial {
Ejemplo:
...
public synchronized void agregar(String msg) {
mensaje[pos] = msg;
pos++;
}
}

Objeto

Al momento de entrar a un mtodo synchronized de un


objeto compartido, un thread se encontrar con una de las
siguientes situaciones:

Mutex

Mutex libre: el thread tomar el mutex, ejecutar el mtodo y lo


liberar slo al momento de terminar la ejecucin de dicho mtodo.
Mutex tomado por otro thread: el thread se bloquear en espera de
que el primero lo libere.

Threads
Franco Guidi Polanco

09-03-2007

45

Bloqueo del objetos compartidos: mtodos


synchronized (cont.)

Franco Guidi Polanco

09-03-2007

46

Bloqueo del objetos compartidos: mtodos


synchronized (cont.)

Consecuencia: slo un thread a la vez podr ejecutar un


mtodo synchronized sobre un objeto. Al interrumpirse la
ejecucin de un thread que accede a un mtodo
synchronized, el paso se dar a otro thread que no
requiera el mutex sobre tal objeto (i.e. que no est
solicitando la ejecucin de cualquiera de sus mtodos
synchronized).
Una vez liberado el mutex, los threads bloquados en espera
de l se vuelven ejecutables.
Objeto

En el caso de nuestro ejemplo, mientras un thread


se encuentra ejecutando el mtodo agregar,
ningn otro thread puede ejecutar dicho mtodo
(pues la ejecucin de agregar requiere el mutex
del objeto historial).
Thread
Hola

mutex

historial.agregar()

Thread
Chao

historial.agregar()

historial

mensaje[pos]=msg;
pos++;

Franco Guidi Polanco

09-03-2007

47

Franco Guidi Polanco

09-03-2007

48

Bloqueo del objetos compartidos: mtodos


synchronized (cont.)

Mtodos synchronized: ejemplo

La invocacin a sleep dentro de un thread no


libera el mutex de los objetos que eventualmente
pudiera tener en su poder.
Zzz

En el ejemplo de la clase Historial, el mtodo


agregar requiere ser synchronized, en cambio
getCapacidad no lo requiere:
public class Historial {
String[] mensajes = new String[1000];
int pos = 0;
public synchronized void agregar(String msg) {
mensaje[pos] = msg;
pos++;
}
public int getCapacidad(){
return mensajes.length;
}

Objeto

Franco Guidi Polanco

09-03-2007

49

En la clase Historial, el mtodo que retorna el


nmero de elementos ingresados al arreglo debe
ser synchronized?
public class Historial {
String[] mensajes = new String[1000];
int pos = 0;
public synchronized void agregar(String msg) {
mensaje[pos] = msg;
pos++;
}
public int getCapacidad(){
return mensajes.length;
}
?
public synchronized int getElementos() {
return pos;
}
}
09-03-2007

09-03-2007

50

Cundo deben declararse mtodos


synchronized?

Mtodos synchronized

Franco Guidi Polanco

Franco Guidi Polanco

En actualizaciones sobre variables de instancia de


objetos que no sean operaciones atmicas:
actualizacin de dos o ms variables
actualizacin de variables long, double
otros?

El costo de declarar mtodos synchronized es:


mayor lentitud de la ejecucin de mtodos (por la
adquisicin del mutex)
peligro de deadlock: bloqueo mutuo de dos threads que
esperan adquirir mutex intercambiados

51

Franco Guidi Polanco

09-03-2007

52

Ejemplo de deadlock

Ejemplo de deadlock (cont.)

Dadas las clases Batman y Robin:

y una aplicacin que instancia dichas clases y las usa


como objetos compartidos por diferentes threads:

public class Batman {


Robin robin;
public void setAsistente( Robin robin ){
this.robin = robin;
}
public synchronized void vuelveABaticueva(){
robin.subeAlBatimovil();
}
public synchronized usaBatiboomerang(){
// usa el batiboomerang como arma de defensa
}
}

public class CiudadGotica {


public static void main( String[] arg){
Batman batman = new Batman();
Robin robin = new Robin();
batman.setAyudante( robin );
robin.setjefe( batman );
// aqu se inician distintas operaciones con threads,
// entre ellos un thread Alfred y otro thread Pingino

public class Robin {


Batman batman;
public void setJefe( Batman batman ){
this.batman = batman;
}
public synchronized powTonkPafBonk(){
batman.usaBatiboomerang();
}
}
Franco Guidi Polanco

09-03-2007

}
}

NOTA:
Ejemplo adaptado de Programming Java Threads in the real world (Parte 2),
Allan Holub, disponible on-line: http://www.javaworld.com
53

Franco Guidi Polanco

Ejemplo de deadlock (cont.)


3.

1.Un thread llamado Alfred invoca el mtodo vuelveABaticueva()


sobre el objeto batman. Alfred obtiene el mutex de batman pero justo
antes de que este mtodo invoque el mtodo subeAlBatimovil() de
robin, su ejecucin es interrumpida.

Franco Guidi Polanco

Entonces el thread Alfred es reactivado, y trata de invocar


subeAlBatimovil() sobre robin. Para invocarlo, sin
embargo, debe adquirir el mutex de robin que est en poder del
thread Pingino.
(Clase Batman)

public synchronized void vuelveABaticueva(){


robin.subeAlBatimovil();
}

4.

2.Otro thread, llamado Pingino, invoca el mtodo powTonkPafBonk()


de robin. El thread Pingino obtiene el mutex de robin, y trata de
ejecutar la instruccin batman.usaBatiboomerang(). Para lograrlo
debe adquirir tambin el mutex de batman, pero como ste est en
poder del thread Alfred, se bloquea en espera de su liberacin.
(Clase Robin)

54

Ejemplo de deadlock (cont.)

Imagine que en el ejemplo anterior ocurre lo siguiente:

(Clase Batman)

09-03-2007

public synchronized void vuelveABaticueva(){


robin.subeAlBatimovil();
}

A este punto el thread Pingino no puede reactivarse porque no


puede obtener el mutex de batman (lo tiene el thread Alfred), ni
tampoco el thread Alfred puede hacerlo, porque no puede
obtener el mutex de robin (lo tiene el thread Pingino)
Deadlock!

public synchronized powTonkPafBonk(){


batman.usaBatiboomerang();
}
09-03-2007

55

Franco Guidi Polanco

09-03-2007

56

Bloques synchronized

Re-adquisicin del mutex


En Java un thread puede re-obtener el mutex de
un objeto que ste ya tiene.
public class NoIncurroEnAutoDeadlock {
public synchronized void a(){
b();
}
public synchronized void b(){
System.out.println( Estoy en b);
}
}

synchronized( objeto ){
// instrucciones
}

Esto evita que un thread incurra en deadlock por


culpa de l mismo.
Franco Guidi Polanco

09-03-2007

57

Bloques synchronized: ejemplo

public class Robot{


Motor motor = new Motor();
...
public void avanzar(){
if( encendido() )
synchronized( motor ){
avanzando = true;
motor.aplicarPotencia( 10 );
...
}
else
preguntar( Desea encender, Si, No);
...
}
}
09-03-2007

Un thread, al ingresar a un bloque synchronized se


bloquea en espera de la adquisicin del mutex asociado al
objeto (o arreglo) declarado en su encabezado. El mutex
es liberado a la salida del bloque.

Franco Guidi Polanco

09-03-2007

58

Bloques synchronized: ejemplo

En el siguiente ejemplo, el mtodo avanzar


adquiere el mutex de motor para ejecutar algunas
instrucciones:

Franco Guidi Polanco

Es posible declarar como synchronized porciones de


cdigo dentro de un mtodo de una clase.
Esto permite implementar exclusin mutua sobre bloques
de instrucciones.
Formato:

59

Consecuencia: dado que en el bloque


synchronized es adquirido el mutex de motor,
ningn otro thread que requiera dicho mutex podr
ser ejecutado concurrentemente.
En consecuencia se obtiene un acceso con
exclusin mutua sobre la instancia de Motor, aun
cuando esta clase no tenga ningn bloque ni
mtodo synchronized.

Franco Guidi Polanco

09-03-2007

60

Bloques y mtodos synchronized

Bloques synchronized (cont.)

El objeto asociado al bloque synchronized puede ser la


misma instancia sobre la cual se ejecuta el mtodo
(referencia this).
Como consecuencia, las siguientes implementaciones son
equivalentes:
public class MiClase{
public synchronized void miMetodo(){
// Instrucciones
}
}
public class MiClase{
public void miMetodo(){
synchronized( this ){
// Instrucciones
}
}
}
Franco Guidi Polanco

09-03-2007

61

Se pueden crear exclusiones en/entre mtodos especficos:


public class Acta{
double[] notasCatedra = new double[10];
double[] notasAyudantia = new double[10];
public void actualizarCatedra(){
synchronized( notasCatedra );
// Instrucciones
}
}
public void imprimirCatedra(){
synchronized( notasCatedra );
// Instrucciones
}
}
public void actualizarAyudantia(){
synchronized( notasAyudantia );
// Instrucciones
}
public void imprimirAyudantia(){
synchronized( notasAyudantia );
// Instrucciones
}
}
}
Franco Guidi Polanco

Sincronizacin de threads

Un thread (Productor) genera un elemento que es


agregado a un depsito, este elemento es consumido
por otro thread (Consumidor)
El depsito tiene capacidad limitada, cuando est lleno,
el Productor debe esperar que se disponga de espacio
nuevamente. Por su parte el consumidor debe esperar
que haya elementos para poder retirarlos.

09-03-2007

Excluyentes

09-03-2007

62

Sincronizacin de threads

El problema consiste en lograr que un thread acte


slo cuando otro ha concluido cierta actividad (y
viceversa): threads mutuamente excluyentes.
Problema del productor/consumidor:

Franco Guidi Polanco

Excluyentes

63

Supongamos que la capacidad del depsito es


igual a 1:

Productor

Depsito

Consumidor

El problema es ms complejo que en los ejemplos


anteriores: el depsito no slo debe soportar
acceso concurrente, sino que los threads deben
tambin actuar sincronizadamente.
Franco Guidi Polanco

09-03-2007

64

Sincronizacin de threads: una solucin


simplista

Ejemplo de sincronizacin
Supongamos la siguiente implementacin de un
Productor y un Consumidor:
public class Productor extends Thread {
private Deposito deposito;
public Productor(Deposito d) {
deposito = d;
}
public void run() {
for (int i=1;i<20 ;i++ )
deposito.guardar();
}
}

public class Consumidor extends Thread{


private Deposito deposito;
public Consumidor(Deposito d) {
deposito = d;
}
public void run() {
for (int i=1;i<20 ;i++ )
deposito.sacar();
}
}

Ambos actan sobre un objeto compartido de la


clase Deposito.

Franco Guidi Polanco

09-03-2007

65

Sincronizacin de threads: una solucin


simplista (cont.)
Problema:
alto consumo de recursos (CPU) en procesos
improductivos.
Solucin ms adecuada:
detener los threads hasta que se den las
condiciones para que acten.

Una solucin simple sera que el productor


verificara cada vez si hay espacio en el depsito, y
si lo hay, entonces agregara un elemento a l.
Lo mismo podra hacer el consumidor antes de
intentar sacar un elemento del depsito.
public class Deposito{
private int elementos = 0;
public synchronized void guardar() {
if( elementos = 0 )
elementos++;
return;
}
public synchronized void sacar() {
if( elementos > 0 )
elementos--;
return;
}
}
Franco Guidi Polanco

09-03-2007

66

Sincronizacin de threads: uso de mtodos


wait y notify
La clase Object provee el mtodo wait() que detiene un
thread hasta que le sea notificada la posibilidad de continuar.
El mtodo wait debe ser invocado sobre un objeto
compartido por los threads a sincronizar (ej. el depsito)
Para poder invocar el mtodo wait es necesario que el
thread tenga el mutex del objeto compartido
La invocacin de wait detiene el thread, lo pone en una lista
de espera asociada al objeto, y libera su mutex.
Objeto

Lista de espera

CPU

Franco Guidi Polanco

09-03-2007

wait

67

Franco Guidi Polanco

09-03-2007

68

Sincronizacin de threads: uso de mtodos


wait y notify (cont.)
El thread saldr de la lista de espera cuando otro thread
invoque el mtodo notify sobre el objeto compartido. Al
salir de la lista de espera, se bloquear en espera del
mutex del objeto para continuar su ejecucin.
Una vez re-obtenido el mutex del objeto, el thread que sali
de la lista de espera continuar la ejecucin del mtodo en
la instruccin siguiente al llamado a wait.
Si hay ms de un thread en la lista de espera, notify reactivar slo uno de ellos. El criterio de seleccin del
thread a re-activar depende de la implementacin de Java.
Nota: El mtodo wait puede generar una
InterruptedException.

Franco Guidi Polanco

09-03-2007

69

Sincronizacin de threads: uso del mtodo


notifyAll

Lista
de
espera
llegu
llegu

Pedro

llegu
Inscripcin para
notificacin (wait)
Franco Guidi Polanco

09-03-2007

Notar que en este modelo la notificacin es


indirecta: el thread que invoca notify no tiene
ninguna referencia al thread que est en espera.
La notificacin acta sobre un objeto compartido, y
al ser este notificado, un thread en espera es
Lista
reactivado.
de
espera
llegu

llegu

lleg Pedro

Pedro

Pedro

Inscripcin para
notificacin (wait)
Franco Guidi Polanco

09-03-2007

70

Ejemplo de sincronizacin (cont.)

El mtodo notifyAll permite reactivar todos los


threads bloqueados en la lista de espera de un
objeto, esto es, se vuelven todos ejecutables.
Notar sin embargo que ellos podrn tomar el
mutex slo de uno a la vez.

llegu

Sincronizacin de threads: uso de mtodos


wait y notify (cont.)

71

El depsito est implementado de la siguiente forma:


public class Deposito{
private int elementos = 0;
public synchronized void guardar() {
try{
if( elementos > 0) // Ms adelante se ver que no est correcto
this.wait();
} catch( InterruptedException e ){}
elementos++;
System.out.println( "Guardar - numero elementos: " + elementos );
this.notify();
}
public synchronized void sacar() {
try{
if( elementos == 0) // Ms adelante se ver que no est correcto
this.wait();
} catch( InterruptedException e ){}
elementos--;
System.out.println( "Sacar - numero elementos: " + elementos );
this.notify();
}
}
Franco Guidi Polanco

09-03-2007

72

Ejemplo de sincronizacin (cont.)

Ejemplo de sincronizacin (cont.)

La aplicacin crea dos threads (Productor y


Consumidor), que accesan el objeto compartido
(depsito):
public class EjemploProductorConsumidor{
public static void main( String[] arg ) {
Deposito deposito = new Deposito();
Productor productor = new Productor( deposito );
Consumidor consumidor = new Consumidor( deposito );
productor.start();
consumidor.start();
}
}

Franco Guidi Polanco

09-03-2007

73

Ejemplo de sincronizacin (cont.)

Se bloquea en espera del mutex


de depsito

Mutex

Lista de
espera

depsito

Thread
Consumidor

Thread
Productor
Verifica que depsito est
vaco y agrega un elemento.

Adquiere el mutex de depsito


Verifica que depsito tenga
elementos. Dado que no los tiene
invoca wait del objeto
depsito.
La invocacin de wait libera el
mutex de depsito y el thread se
agrega a la lista de espera (de
depsito).

09-03-2007

74

75

Mutex

Lista
de espera

Thread
Consumidor

depsito

Invoca notify de depsito.


Termina la ejecucin del mtodo y
libera el mutex.
Se bloquea en espera del mutex
de depsito

Sale de la lista de espera y se bloquea en


espera del mutex de depsito.
Adquiere el mutex de depsito.
Saca el elemento del depsito

Invoca notify de depsito (*)

Adquiere el mutex de deposito

Franco Guidi Polanco

09-03-2007

Ejemplo de sincronizacin (cont.)

Supongamos en un momento cualquiera que el depsito


est vaco y se activa el thread Consumidor
Thread
Productor

Franco Guidi Polanco

Franco Guidi Polanco

(*) El efecto de notify en esta


secuencia es nulo, pero qu habra
pasado si el mutex de depsito lo
hubiera ganado el Productor?
09-03-2007

76

Sincronizacin y bloqueo iterativo (spin lock)

En la implementacin de Deposito existe aun un


problema. Suponga que hay mas de un thread
Consumidor, el depsito est vaco, y se da la siguiente
secuencia de eventos:
1.

Un thread Consumidor toma el mutex del depsito, y


verifica la existencia de un elemento en l. Dado que el
depsito est vaco, invoca el mtodo wait (sobre el
depsito), se bloquea en la lista de espera (de
depsito), y libera su mutex.
if( elementos == 0)
this.wait();

2.

El thread Productor es reactivado, adquiere el mutex de


depsito, comprueba que ste est vaco, le agrega un
elemento, e invoca notify (sobre el objeto depsito).

Franco Guidi Polanco

09-03-2007

77

Sincronizacin y bloqueo iterativo (spin lock)


Este problema nace del hecho que la especificacin
de Java no establece que la salida de la lista de
espera y adquisicin del mutex sean implementados
como una operacin atmica (distintas JVM se
pueden comportar de distinto modo).
Problemas anlogos se presentan cuando:
Hay ms de un Productor.
La notificacin ocurre con notifyAll en vez de notify.

Franco Guidi Polanco

09-03-2007

79

Sincronizacin y bloqueo iterativo (spin lock)


3.
4.
5.

6.

El thread Consumidor que estaba en la lista de espera de


depsito es notificado (sacado de dicha lista), y puesto
en espera del mutex (de depsito).
El thread Productor libera el mutex de depsito.
Otro thread Consumidor, que no estaba en la lista de
espera de depsito, adquiere su mutex, comprueba que
el depsito tiene un elemento, lo saca del depsito,
invoca notify (no interesa que ocurre con esto), y libera
el mutex de depsito.
El primer thread Consumidor (que en el paso 3 haba sido
sacado de la lista de espera y bloqueado en espera del
mutex) adquiere el mutex de depsito (antes de que el
thread Productor trate de agregar un elemento), y contina
su ejecucin en el punto en que estaba: trata de sacar un
elemento, pero el depsito est vaco: ERROR.

Franco Guidi Polanco

09-03-2007

78

Sincronizacin y bloqueo iterativo (spin lock)

Solucin al problema anterior:


reemplazar la estructura:
if( condicin de detencin )
wait();

por:
while( condicin de detencin )
wait();

Franco Guidi Polanco

09-03-2007

Spin lock

80

Para saber ms...

Sincronizacin y bloqueo iterativo (spin lock)

The Java Tutorial (Sun Microsystems)

Es decir, la clase Deposito queda:

http://java.sun.com

public class Deposito{


private int elementos = 0;
public synchronized void guardar() {
try{
while( elementos > 0)
this.wait();
} catch( InterruptedException e ){}
elementos++;
System.out.println( "Guardar - numero elementos: " + elementos );
this.notify();
}
public synchronized void sacar() {
try{
while( elementos == 0)
this.wait();
} catch( InterruptedException e ){}
elementos--;
System.out.println( "Sacar - numero elementos: " + elementos );
this.notify();
}
}
Franco Guidi Polanco

09-03-2007

The Java API (Sun Microsystems)


http://java.sun.com

Programming Java Threads in the Real


World (Parts 1-9) Allan Hollub.
http://www.javaworld.com

81

Franco Guidi Polanco

09-03-2007

82

Vous aimerez peut-être aussi