Sie sind auf Seite 1von 24

ITSON Manuel Domitsu Kono

Tema 12
Hilos en Java
Procesos e Hilos

En la programacin concurrente, hay dos unidades bsicas de ejecucin: procesos
e hilos. En el lenguaje de programacin J ava, la programacin concurrente est
ms relacionada con los hilos. Sin embargo, los procesos tambin son
importantes.

Un Proceso es un ambiente de ejecucin autocontenido. Por lo general tiene
recursos de ejecucin completos y privados, en particular cada proceso tiene su
propio espacio de memoria.

A veces se ve a los procesos como sinnimo de programas o aplicaciones. Sin
embargo, una aplicacin puede ser un conjunto de procesos cooperativos. Para
facilitar la comunicacin entre procesos, la mayora de los sistemas operativos
soportan recursos de Comunicacin entre Procesos, IPC, como tubos y sockets.
IPC no slo se usa para comunicar procesos en un solo sistema sino procesos en
diferentes sistemas.

Un Hilo, llamado a veces proceso ligero, provee tambin un ambiente de
ejecucin. Pero crear un nuevo hilo requiere de menos recursos que crear un
nuevo proceso.

Los hilos existen dentro de los procesos cada proceso tiene al menos un hilo. Un
proceso con un solo hilo tiene las siguientes propiedades:

El proceso empieza su ejecucin en un punto bien conocido. En los
programas como C, C++o J ava el proceso empieza su ejecucin en la
primera sentencia del mtodo mai n( ) .
La ejecucin de las sentencias sigue una secuencia predefinida
completamente ordenada para un conjunto de entradas.
Durante la ejecucin, el proceso tiene acceso a ciertos datos. En J ava hay
tres tipos de datos a los que un proceso puede acceder: variables locales
que se encuentran en la pila del hilo. Variables de instancia que se accesan
mediante referencias y variables estticas que se acceden mediante clases
o referencias a objetos.
430 Hilos en Java
ITSON Manuel Domitsu Kono
En la figura 11.1 se ilustra un ejemplo de un sistema multitarea, en el cual hay
varias aplicaciones en ejecucin. Cada uno de ellos con un solo hilo de ejecucin.
En este caso tenemos varios procesos ejecutndose simultneamente.


Figura 12.1

Aunque para el usuario, los procesos parecen estar ejecutndose
simultneamente, esto solo puede ocurrir en un sistema con varios procesadores
o con un procesador con varios ncleos de ejecucin. En el caso de un sistema
con un solo procesador mononcleo, la apariencia de ejecucin simultnea se
logra compartiendo el procesador entre los diferentes procesos asignndole a
cada uno de ellos una ranura de tiempo y cambiando a ejecutar otro proceso
cuando el tiempo de la ranura se ha agotado.

En un sistema multitarea que soporta multihilos, los hilos comparten los recursos
de un proceso, incluyendo la memoria y los archivos abiertos. Esto hace la
comunicacin eficiente pero potencialmente problemtica. En este caso cada
proceso puede tener uno o ms hilos. Los diferentes procesos y sus hilo pueden
ejecutarse simultneamente en forma real o aparente, dependiendo del nmero de
procesadores o ncleos.

En un proceso, los hilos mltiples tienen las siguientes propiedades:

Cada hilo empieza su ejecucin en un punto bien conocido. Para alguno de
esos hilos ese punto es la primera sentencia del mtodo mai n( ) . Para el
resto de los hilos, el programador decide ese punto al codificar el hilo.
Cada hilo ejecuta su cdigo desde su punto inicial en una forma ordenada y
predefinida para un conjunto de entradas.
Cada hilo ejecuta su cdigo independientemente de los otros hilos en el
programa. Si se requiere existen mecanismos para que los hilos cooperen
entre s.
Los hilos aparentar ejecutar con cierto grado de simultaneidad.
Los hilos tienen acceso a varios tipos de datos. Cada hilo tiene sus propias
variables locales. Los hilos pueden compartir las variables de instancia. Los
hilos comparten en forma automtica a las variables estticas.

Tema 12 Hilos en Java 431
ITSON Manuel Domitsu Kono
En la figura 12.2 se ilustra un ejemplo de un sistema multitarea, en el cual hay
varias aplicaciones en ejecucin. Cada uno de ellos puede tener varios hilos de
ejecucin. En este caso, uno de esos procesos es la mquina virtual de java.


Figura 12.2

La ejecucin multihilo es una caracterstica esencial de la plataforma de J ava.
Cada aplicacin tiene al menos un hilo o varias si se cuentan los hilos del sistema
que administran la memoria y el manejo de las seales. Pero desde el punto de
vista del programador se empieza con un hilo, llamado el hilo principal. Este hilo
tiene la habilidad de crear hilos adicionales como se ver ms adelante.

La API de Java para Hilos

Para comprender el funcionamiento de un programa multihilo veamos primero un
programa con un solo hilo de ejecucin. Considere el el siguiente programa
formado por dos clases:

Hilo.java
publ i c cl ass Hi l o {
publ i c voi d r un( ) {
f or ( i nt i = 0; i < 100; i ++) {
Syst em. out . pr i nt l n( i + " : Hol a" ) ;
}
}
}


432 Hilos en Java
ITSON Manuel Domitsu Kono
Prueba.java
publ i c cl ass Pr ueba {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Hi l o hi l o = new Hi l o( ) ;

hi l o. r un( ) ;
}
}

En la figura 12.3 se muestra el comportamiento del programa con el tiempo. En
este caso como slo hay un hilo de ejecucin las sentencias se ejecutan una
despus de la otra. No hay concurrencia.


Figura 12.3

Para hacer que el mtodo r un( ) ejecute en paralelo con el mtodo mai n( ) y
otros mtodos de la clase Prueba, hay que modificar la clase Hilo para que sea
ejecutado por un nuevo hilo. Para ello nuestra clase Hilo deber heredar de la
clase Thr ead. Como se muestra en el siguiente cdigo:

Hilo.java
publ i c cl ass Hi l o ext ends Thr ead {
publ i c voi d r un( ) {
f or ( i nt i = 0; i < 100; i ++) {
Syst em. out . pr i nt l n( I + " : Hol a" ) ;
}
}
}

Tambin debemos modificar a la clase que invoca a la clase Hi l o para que en
lugar de invocar a su mtodo r un( ) invoque a su mtodo st ar t ( ) .



Prueba.java
Tema 12 Hilos en Java 433
ITSON Manuel Domitsu Kono
publ i c cl ass Pr ueba {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Hi l o hi l o = new Hi l o( ) ;

hi l o. st ar t ( ) ;
}
}

El mtodo st ar t ( ) es quien crea otro hilo de ejecucin. Este hilo, despus de un
proceso de inicializacin invoca al mtodo r un( ) . Este mismo hilo, cuando el
mtodo r un( ) completa se encarga de os detalles de terminacin del hilo. El
mtodo st ar t ( ) del hilo original regresa inmediatamente, por lo que el mtodo
r un( ) ejecutar en el nuevo hilo al mismo tiempo que el mtodo st ar t ( )
regresa en el primer hilo como se muestra en la figura 12.4.


Figura 12.4

La clase Thr ead forma parte de la API de J ava para hilos. Parte de esa API se
muestra en la figura 12.5.

Tabla 12.1 Atributos de la Clase Thread
publ i c st at i c f i nal i nt MAX_PRIORITY

La mxima prioridad que puede tener un hilo
publ i c st at i c f i nal i nt MIN_PRIORITY

La mnima prioridad que puede tener un hilo
publ i c st at i c f i nal i nt NORM_PRIORITY

La prioridad por ausencia, que puede tener un hilo

434 Hilos en Java
ITSON Manuel Domitsu Kono

Figura 12.4

Tabla 12.2 Mtodos de la Clase Thread.
publ i c Thr ead( )
publ i c Thr ead( Runnabl e t ar get )
publ i c Thr ead( Runnabl e t ar get , St r i ng name)
publ i c Thr ead( St r i ng name)

Crean nuevos hilos. El parmetro target denota el objeto del cual se ejecuta el mtodo r un( ) .El
valor por omisin es nul l . El parmetro name establece el nombre del nuevo hilo. El valor
prestablecido es Thr ead- n, donde n es un entero consecutivo.
publ i c voi d start( )

Hace que este hilo inicie su ejecucin; La mquina virtual de J ava llama al mtodo r un( ) de este
hilo. Como resultado hay dos hilos ejecutndose concurrentemente. El hilo actual (que regresa de
la llamada al mtodo st ar t ( ) y el otro hilo (que ejecuta su mtodo r un( ) ).

Es ilegal iniciar un hilo ms de una vez. En particular un hilo no puede reiniciarse una vez que
completa su ejecucin.

Lanza:
I l l egal Thr eadSt at eExcept i on Si el hilo ya ha sido iniciado.
publ i c voi d run( )

Si este hilo fue construido usando un objeto Runnabl e diferente, entonces se invoca al mtodo
r un( ) del objeto. De otra forma, el mtodo no hace nada y regresa.
Tema 12 Hilos en Java 435
ITSON Manuel Domitsu Kono
Hilos Usando la Interfaz Runnable
Hay ocasiones en que crear un hilo heredando de la clase Thr ead no es
conveniente. Por ejemplo, si la clase que deseamos que sea un hilo, es una
subclase, no podemos hacer que herede de la clase Thr ead ya que una clase
slo puede heredar de una clase. En estos casos, en vez de hacer que la clase
herede de la clase Thr ead haremos que la clase implemente la interfaz
Runnabl e, como se muestra en el siguiente cdigo:

HiloRunnable.java
publ i c cl ass Hi l oRunnabl e i mpl ement s Runnabl e {
publ i c voi d r un( ) {
f or ( i nt i = 0; i < 100; i ++) {
Syst em. out . pr i nt l n( i + " : Hol a desde l a cl ase Hi l o" ) ;
}
}
}

Una clase que implemente la interfaz debe implementar el mtodo run(). Sin
embargo para crear un nuevo hilo no es suficiente con crear una instancia de la
clase que implementa la interfaz Runnabl e e invocar a su mtodo st ar t ( ) ya
que la clase no hereda de la clase Thr ead y no tiene el mtodo st ar t ( ) . En
lugar de ello crearemos un nuevo hilo y le pasaremos al constructor de la clase
Thr ead la instancia de la clase que implementa la interfaz Runnabl e, como se
muestra en el siguiente cdigo:

PruebaHiloRunnable.java
publ i c cl ass Pr uebaHi l oRunnabl e {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Runnabl e hi l oRunnabl e = new Hi l o( ) ;
Thr ead hi l o = new Thr ead( hi l oRunnabl e) ;

hi l o. st ar t ( ) ;
}
}

La razn para pasarle un objeto Runnabl e al constructor de la clase Thr ead es
para que el hilo tenga acceso al mtodo r un( ) del objeto Runnabl e para que lo
ejecute. El mtodo st ar t ( ) del hilo llama al mtodo r un( ) de la clase Thr ead
ya que no fue sobrescrito, que a su vez llama al mtodo r un( ) del objeto
Runnabl e ya que el mtodo r un( ) por ausencia de la clase Thr ead tiene la
forma:

publ i c voi d r un( ) {
i f ( t ar get ! = nul l ) t ar get . r un( ) ;
}

436 Hilos en Java
ITSON Manuel Domitsu Kono
Donde target es el objeto Runnabl e que le pasamos al constructor del hilo. As
que el hilo empieza la ejecucin de su mtodo run() quien inmediatamente llama al
mtodo run() del objeto Runnabl e.
Suspender y Parar un Hilo

La ejecucin de un hilo puede ser suspendida temporalmente, invocando a su mtodo
sleep(), tabla 12.3.

Tabla 12.3 Mtodos de la Clase Thread. Cont.
publ i c st at i c voi d sleep( l ong mi l l i s) t hr ows I nt er r upt edExcept i on
publ i c st at i c voi d sleep( l ong mi l l i s, i nt nanos)
t hr ows I nt er r upt edExcept i on

Hace que el hilo, actualmente en ejecucin se duerma (cese temporalmente su ejecucin) por el
nmero de milisegundos (ms el nmero de nanosegundos, en el caso del segundo mtodo) dado
por el parmetro, sujeto a la precisin y exactitud de los temporalizadores y el programador
tareas. El hilo no pierde la propiedad de ninguno de sus monitores.

El valor del parmetro nanos debe estar en el rango de 0 999999 nanosegundos.

Lanza:
I nt er r upt edExcept i on Si cualquier hilo ha interrumpido este hilo. El estado
interrumpido del hilo actual se limpia cuando se lanza esta excepcin.
I l l egal Ar gument Except i on Si el valor de mi l l i s es negativo o el valor de nanos
no est en el rango de 0 999999 nanosegundos.
publ i c f i nal bool ean isAlive( )

Checa si el hilo est vivo. Un hilo est vivo si ha sido iniciado y no ha muerto.

El siguiente cdigo muestra el uso del mtodo sl eep( ) de la clase Thr ead para
implementar un hilo que ejecuta una tarea peridicamente. En este ejemplo, el
mtodo suspende la ejecucin del hilo por 1000 ms por lo que el ciclo despliega el
mensaje aproximadamente cada 1 s mientras se repita el ciclo.

Este cdigo tambin ilustra la forma de parar un hilo. Para parar un hilo hay que
hacer que su mtodo r un( ) termine su ejecucin y regrese. Para lograr eso en la
clase principal hacemos que la variable vivo tome el valor de falso, lo que har que
el ciclo termine y termine tambin el mtodo r un( ) . Tambin en la clase principal
le asignamos a la referencia al hilo, t empor i zador , el valor de null para que el
recolector de basura destruya al objeto y libere los recursos que el hilo tena
asignados.

Temporizador.java
publ i c cl ass Tempor i zador ext ends Thr ead {
publ i c vol at i l e bool ean vi vo;

publ i c Tempor i zador ( ) {
vi vo = t r ue;
Tema 12 Hilos en Java 437
ITSON Manuel Domitsu Kono
}

publ i c voi d r un( ) {
whi l e ( vi vo) {
t r y {
Syst em. out . pr i nt l n( " Despi er t o" ) ;
sl eep( 1000) ;
} cat ch ( Except i on e) {
}
}
}
}

PruebaTemporizador.java
publ i c cl ass Pr uebaTempor i zador {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Tempor i zador t empor i zador = new Tempor i zador ( ) ;

t empor i zador . st ar t ( ) ;

t r y {
Thr ead. sl eep( 10000) ;
}
cat ch( I nt er r upt edExcept i on i e) {
Syst em. out . pr i nt l n( i e. get Message( ) ) ;
}

t empor i zador . vi vo = f al se;
t empor i zador = nul l ;
}

}
El ciclo de Vida de un Hilo

En la figura 12.5 muestra el ciclo de vida de un hilo. Hay un periodo de tiempo
despus de que se invoca al mtodo st ar t ( ) antes de que la mquina virtual de
J ava pueda arrancar al hilo. En forma similar, cuando un hilo regresa de su
mtodo run(), hay un periodo de tiempo antes que la mquina virtual de J ava
pueda liberar los recursos ocupados por el hilo. Este atraso ocurre por que toma
tiempo arrancar o terminar un hilo; luego hay un tiempo de transicin entre el
estado de ejecucin y el estado de parado de un hilo como se muestra en la figura
12.5.

438 Hilos en Java
ITSON Manuel Domitsu Kono

Figura 12.5

Para saber si un hilo ya ha arrancado o si ya terminado podemos llamar al mtodo
i sAl i ve( ) . Por ejemplo, en el siguiente cdigo, nos aseguramos que el hilo ya
haya terminado antes de liberar los recursos que se le hayan asignado:

PruebaTemporizador.java
publ i c cl ass Pr uebaTempor i zador {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Tempor i zador t empor i zador = new Tempor i zador ( ) ;

t empor i zador . st ar t ( ) ;

t r y {
Thr ead. sl eep( 10000) ;
}
cat ch( I nt er r upt edExcept i on i e) {
Syst em. out . pr i nt l n( i e. get Message( ) ) ;
}

t empor i zador . vi vo = f al se;
whi l e ( t empor i zador . i sAl i ve( ) ) {
t r y {
Thr ead. sl eep( 100) ;
} cat ch ( I nt er r upt edExcept i on i e) {
Syst em. out . pr i nt l n( i e. get Message( ) ) ;
}
}

t empor i zador = nul l ;
}
}
Unin de Hilos

En el ejemplo anterior usamos los mtodos i sAl i ve( ) y sl eep( ) de un hilo
para esperar a que el hilo termine su ejecucin antes de liberar los recusos
Tema 12 Hilos en Java 439
ITSON Manuel Domitsu Kono
asignados al hilo. Hay otros mtodos en la API de J ava que son ms adecuados
para esta tarea. A este acto de esperar se le conoce como una unin de hilos. Nos
estamos uniendo con el hilo creado previamente. Para unirnos con un hilo
podemos usar el mtodo j oi n( ) , tabla 12.4.

Tabla 12.4 Mtodos de la Clase Thread. Cont.
publ i c f i nal voi d join( ) t hr ows I nt er r upt edExcept i on

Espera a que este hilo muera.

Lanza:
I nt er r upt edExcept i on Si cualquier hilo ha interrumpido este hilo. El estado
interrumpido del hilo actual se limpia cuando se lanza esta excepcin.
publ i c f i nal voi d join( l ong mi l l i s) t hr ows I nt er r upt edExcept i on
publ i c f i nal voi d join( l ong mi l l i s, i nt nanos) t hr ows I nt er r upt edExcept i on

Espera a que este hilo muera cuando mucho el nmero de milisegundos (ms el nmero de
nanosegundos, en el caso del segundo mtodo) dado por el parmetro. En el primer mtodo, un
valor del parmetro de 0 significa esperar por siempre.

El valor del parmetro nanos debe estar en el rango de 0 999999 nanosegundos.

Lanza:
I nt er r upt edExcept i on Si cualquier hilo ha interrumpido este hilo. El estado
interrumpido del hilo actual se limpia cuando se lanza esta excepcin.
I l l egal Ar gument Except i on Si el valor de mi l l i s es negativo o el valor de nanos
no est en el rango de 0 999999 nanosegundos.

El siguiente cdigo ilustra el uso del mtodo j oi n( ) para esperar que un hilo
termine su ejecucin:

PruebaTemporizador.java
publ i c cl ass Pr uebaTempor i zador {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Tempor i zador t empor i zador = new Tempor i zador ( ) ;

t empor i zador . st ar t ( ) ;

t r y {
Thr ead. sl eep( 10000) ;
}
cat ch( I nt er r upt edExcept i on i e) {
Syst em. out . pr i nt l n( i e. get Message( ) ) ;
}

t empor i zador . vi vo = f al se;

t r y {
t empor i zador . j oi n( ) ;
} cat ch ( I nt er r upt edExcept i on i e) {
Syst em. out . pr i nt l n( i e. get Message( ) ) ;
}

440 Hilos en Java
ITSON Manuel Domitsu Kono
t empor i zador = nul l ;
}
}

Tabla 12.5 Otros Mtodos de la Clase Thread. Cont.
publ i c f i nal St r i ng getName( )

Regresa el nombre de este hilo.
publ i c f i nal voi d setName( St r i ng name)

Cambia el nombre de este hilo por el valor del parmetro.

Lanza:
Secur i t yExcept i on Si el hilo actual no puede modificar el nombre de este hilo.
publ i c st at i c Thr ead currentThread( )

Regresa una referencia al hilo actual en ejecucin.
publ i c st at i c i nt enumerate( Thr ead[ ] t ar r ay)

Copia en el arreglo del parmetro todos los hilos activos.

Regresa:
El nmero de hilos colocados en el arreglo.

Lanza:
Secur i t yExcept i on Si el hilo actual no puede modificar el nombre de este hilo.
publ i c st at i c i nt activeCount( )

Regresa el nmero de hilos actives.
publ i c voi d interrupt( )

Interrumpe este hilo.

Si este hilo est bloqueado en una invocacin de los mtodos wai t ( ) de la clase Obj ect o de
los mtodos j oi n( ) o sl eep( ) de esta clase, entonces su estado interrumpido se limpia y
recibir una excepcin del tipo I nt er r upt edExcept i on. De otra manera el estado interrumpido
se establecer.

Lanza:
Secur i t yExcept i on Si el hilo actual no puede modificar este hilo.
publ i c st at i c bool ean interrupted( )

Prueba si el hilo actual ha sido interrumpido. El mtodo limpia el estado interrumpido del hilo. En
otras palabras, si este mtodo se llama dos veces en consecutivas, la segunda llamada, regresar
falso (a menos que el hilo actual sea interrumpido de nuevo, despus de que la primera llamada
haya limpiado su estado interrumpido y antes de que la segunda llamada lo inspeccione.
publ i c bool ean isInterrupted( )

Prueba si el hilo actual ha sido interrumpido. El mtodo no modifica el estado interrumpido del hilo.
publ i c f i nal i nt getPriority( )

Regresa la prioridad de este hilo.


Tema 12 Hilos en Java 441
ITSON Manuel Domitsu Kono

Tabla 12.5 Otros Mtodos de la Clase Thread. Cont.
publ i c f i nal voi d setPriority( i nt newPr i or i t y)

Cambia la prioridad de este hilo.

Lanza:
I l l egal Ar gument Except i on Si la prioridad no est en el rango de MI N_PRI ORI TY a
MAX_PRI ORI TY.
Throws:
I l l egal Ar gument Except i on - If the priority is not in the range MI N_PRI ORI TY to
MAX_PRI ORI TY.
Secur i t yExcept i on Si el hilo actual no puede modificar este hilo.
publ i c st at i c voi d yield( )

Hace que el hilo en ejecucin actualmente, haga una pausa temporal y permita que otro hilo
ejecute.

Sincronizacin

Al compartir datos entre varios hilos puede ocurrir una situacin llamada
condicin de carrera entre dos hilos intentando acceder al mismo dato a ms o
menos al mismo tiempo. Para ilustrar el problema veamos el siguiente problema:

Suponga que se desea implementar una aplicacin para un cajero automtico. La
primera tarea es disear e implementar el caso de uso que permite a un usuario
retirar efectivo de un cajero. El primer intento de algoritmo puede ser:

1. Verificar que el usuario tenga suficiente efectivo en su cuenta para cubrir el
retiro. Si no lo tiene ir al paso 4.
2. Restar la cantidad a retirar de la cuenta del usuario
3. Entregar el dinero al usuario
4. Imprimir el recibo del usuario.
La implementacin del algoritmo podra ser la siguiente:

publ i c cl ass Caj er oAut omat i co ext ends Caj er o {
publ i c voi d r et i r ar ( f l oat cant i dad) {
Cuent a c = get Cuent a( ) ;

i f ( c. deduci r ( cant i dad) )
ent r egar ( cant i dad) ;
i mpr i mi r Reci bo( ) ;
}

Cuent a get Cuent a( ) {
. . .
}

voi d ent r agar ( f l oat cant i dad) {
. . .
442 Hilos en Java
ITSON Manuel Domitsu Kono
}

voi d i mpr i mi r Reci bo( ) {
. . .
}
}

publ i c cl ass Cuent a {
pr vat e f l oat t ot al ;

publ i c bool ean deduci r ( f l oat t ) {
i f ( t <= t ot al ) {
t ot al - = t ;
r et ur n t r ue;
}

r et ur n f al se;
}
}

La implementacin parece funcionar hasta que dos personas que tengan acceso a
la misma cuenta (cuentas mancomunadas). Un da un esposo y su esposa (por
separado) deciden vaciar la cuenta y por coincidencia lo intentan al mismo
tiempo. En este caso tenemos una condicin de carrera: si los dos usuarios retiran
del banco al mismo tiempo, haciendo que los mtodos se llamen al mismo tiempo,
es posible que los dos cajeros confirmen que la cuenta tiene suficiente dinero y se
los entregue a ambos usuarios. Lo que ocurre es que dos hilos acceden a la base
de datos de la cuenta al mismo tiempo.

La condicin de carrera ocurre porque la accin de verificar la cuenta y cambiar su
estado no es una operacin atmica. El hilo del esposo y el hilo de la esposa
compiten por la cuenta:

1. El hilo del esposo empieza a ejecutar el mtodo deduci r ( ) .
2. El hilo del esposo confirma que la cantidad a deducir es menor o igual al
total de la cuenta.
3. El hilo de la esposa empieza a ejecutar el mtodo deduci r ( ) .
4. El hilo de la esposa confirma que la cantidad a deducir es menor o igual al
total de la cuenta.
5. El hilo de la esposa ejecuta la sentencia de substraer para deducir la
cantidad, regresa verdadero y el cajero entrega el dinero.
6. El hilo del esposo ejecuta la sentencia de substraer para deducir la
cantidad, regresa verdadero y el cajero entrega el dinero.

Para tratar con este problema, el lenguaje J ava provee de la palabra reservada
syncr oni zed que impide que dos hilos ejecuten el mismo cdigo al mismo
tiempo. Si modificamos el mtodo deduci r ( ) de la clase cuenta de la
siguiente forma:


Tema 12 Hilos en Java 443
ITSON Manuel Domitsu Kono
publ i c cl ass Cuent a {
pr vat e f l oat t ot al ;

publ i c syncr oni zed bool ean deduci r ( f l oat t ) {
i f ( t <= t ot al ) {
t ot al - = t ;
r et ur n t r ue;
}

r et ur n f al se;
}
}
Mtodo Sincronizado

En el ejemplo anterior el mtodo deduci r ( ) est sincronizado. Para que un
mtodo sincronizado pueda ejecutarse, primero debe adquirir un token o candado.
Una vez que el mtodo adquiere ese candado, ejecuta su cdigo y al terminar
regresa el candado, sin importar la forma en que termine su ejecucin, incluyendo
va una excepcin. Solo hay un candado por objeto, as que si dos hilos separados
tratan de llamar a un mtodo sincronizado de un mismo objeto, slo uno de ellos
puede ejecutar el mtodo inmediatamente; el otro hilo tiene que esperar a que el
primer hilo libere el candado antes de que pueda ejecutar el mtodo. La sintaxis
de un mtodo sincronizado es la siguiente:

modificadorAcceso syncronized tipo nomMetodo(lista de parmetros) {
declaraciones

sentencias
}
Bloque Sincronizado

En lugar de sincronizar todo un mtodo podemos sincronizar un bloque. Al igual
que con un mtodo sincronizado, un bloque sincronizado debe adquirir primero un
candado para poder ejecutarse, al terminar la ejecucin del bloque, regresa el
candado. Por lo tanto si hay dos hilos tratando de ejecutar el bloque, slo uno de
los hilos lo ejecutar inmediatamente, el otro hilo deber esperar a que el primer
hilo termine la ejecucin del bloque y libere el candado para que pueda ejecutar el
bloque. La sintaxis de un bloque sincronizado es la siguiente:

syncronized(expresin) {
declaraciones

sentencias
}

Donde expresin debe ser una expresin que se evalu a un objeto, el objeto del
que se obtiene el candado.

444 Hilos en Java
ITSON Manuel Domitsu Kono
Declarar un mtodo como sincronizado es equivalente a:

modificadorAcceso tipo nomMetodo(lista de parmetros) {
syncronized(this) {
declaraciones

sentencias
}
}
Esperar y Notificar
La clase Obj ect tiene los mtodos wai t ( ) y not i f y( ) . Esos mtodos permiten
que un hilo libere el candado en un momento arbitrario, y espere a que otro hilo se
lo regrese antes de continuar. Estas actividades deben ocurrir dentro de bloques
sincronizados.

Tabla 12.4 Mtodos de la Clase Object Empleados con Hilos
publ i c f i nal voi d notify( )

Despierta un solo hilo que est esperando por el monitor de este objeto. Si hay varios hilos
esperando por este objeto, uno de ellos es seleccionado para despertarse. La seleccin es
arbitraria y ocurre a la discrecin de la implementacin. Un hilo espera por el monitor del objeto al
llamar a alguno de sus mtodos wai t ( ) .

El hilo despertado no podr proceder hasta que el hilo actual libere el candado sobre el objeto. El
hilo despertado competir con cualquier otro hilo que este compitiendo activamente para
sincronizarse con este objeto; por ejemplo, el hilo despertado no tiene privilegios o desventajas
para ser el siguiente hilo en ponerle un candado al objeto.

Este mtodo slo debe llamarlo un hilo que sea el dueo del monitor del objeto. Un hilo se vuelve
el dueo del monitor del objeto en una de tres formas:

Ejecutando una mtodo de instancia sincronizado del objeto.
Ejecutando el cuerpo de una sentencia sincronizada que sincroniza en el objeto.
En los objetos de tipo Class, ejecutando un mtodo esttico de la clase.

Slo uno de los hilos a la vez puede ser el dueo del monitor del objeto.

Lanza:
I l l egal Moni t or St at eExcept i on Si el hilo actual no es el dueo del monitor del
objeto.










Tema 12 Hilos en Java 445
ITSON Manuel Domitsu Kono
Tabla 12.4 Mtodos de la Clase Object Empleados con Hilos. Cont.
publ i c f i nal voi d notifyAll( )

Despierta a todos los hilos que estn esperando por el monitor del objeto. Un hilo espera por el
monitor del objeto al llamar a alguno de sus mtodos wai t ( ) .

Los hilos despertados no podrn proceder hasta que el hilo actual libere el candado sobre el
objeto. Los hilos despertados competirn con cualquier otro hilo que este compitiendo activamente
para sincronizarse con este objeto; por ejemplo, el hilo despertado no tiene privilegios o
desventajas para ser el siguiente hilo en ponerle un candado al objeto.

Este mtodo slo debe llamarlo un hilo que sea el dueo del monitor del objeto.

Lanza:
I l l egal Moni t or St at eExcept i on Si el hilo actual no es el dueo del monitor del
objeto.
publ i c f i nal voi d wait( l ong t i meout ) t hr ows I nt er r upt edExcept i on

Hace que el hilo actual espere hasta que otro hilo invoque al mtodo not i f y( ) o al mtodo
not i f yAl l ( ) de este objeto, o la cantidad de milisegundos especificada por el parmetro haya
transcurrido.

El hilo actual debe poseer el monitor de este objeto.

Este mtodo hace que el hilo actual se coloque en el estado de espera para este objeto y luego
libere cualquier derecho de sincronizacin en este objeto. El hilo deja de estar disponible para el
planificador de procesos y permanece dormido hasta que alguna de estas cuatro cosas suceden:

Algn otro hilo invoca al mtodo not i f y( ) para este objeto y el hilo es seleccionado
arbitrariamente para ser el hilo a despertar.
Algn otro hilo invoca al mtodo not i f yAl l ( ) para este objeto.
Algn otro hilo interrumpe al hilo.
El tiempo en milisegundos especificado por el parmetro ha transcurrido (ms o menos).
Si t i meout es cero, el hilo espera hasta ser notificado.

El hilo es removido del estado de espera para este objeto y queda disponible para el planificador
de procesos. Luego compite con los otros hilos por el derecho de sincronizarse con el objeto. Una
vez que ha ganado el control del objeto, todos sus derechos de sincronizacin en el objeto se
restablecen al estado que tenan en el momento en que se invoco al mtodo wai t ( ) . El hilo luego
regresa del mtodo wai t ( ) .

Lanza:
I l l egal Ar gument Except i on Si el valor de t i meout es negativo.
I l l egal Moni t or St at eExcept i on Si el hilo actual no es el dueo del monitor del
objeto.
I nt er r upt edExcept i on Si cualquier hilo ha interrumpido el corriente hilo antes o
mientras el hilo actual est esperando por una notificacin. El estado de interrumpido del
hilo actual se limpia cuando se lanza la excepcin.





446 Hilos en Java
ITSON Manuel Domitsu Kono
Tabla 12.4 Mtodos de la Clase Object Empleados con Hilos. Cont.
publ i c f i nal voi d wait( l ong t i meout , i nt nanos)
t hr ows I nt er r upt edExcept i on

Hace que el hilo actual espere hasta que otro hilo invoque al mtodo not i f y( ) o al mtodo
not i f yAl l ( ) de este objeto, u otro hilo interrumpa al hilo actual o la cantidad de tiempo dado
por sus parmetros haya transcurrido.

El mtodo es similar al mtodo wait con un solo parmetro, pero permite un control ms fino sobre
la cantidad de tiempo a esperar por la notificacin antes de continuar. El tiempo a esperar medido
en nanosegundos est dada por:

1000000*t i meout +nanos

En todos los dems aspectos, este mtodo es similar al mtodo wai t ( l ong
t i meout ) . En particular wai t ( 0, 0) significa lo mismo que wai t ( 0) .
publ i c f i nal voi d wait( ) t hr ows I nt er r upt edExcept i on

Este mtodo se comporta como si se llamara a wai t ( 0) .

Al ejecutar el mtodo wai t ( ) desde un bloque sincronizado, un hilo libera el
candado y se duerme. Un hilo puede hacer eso si necesita esperar que pase algo
en otra parte de la aplicacin. Ms tarde cuando el evento esperado pase, el hilo
que est en ejecucin llama al mtodo not i f y( ) desde un bloque sincronizado
en el mismo objeto. Entonces el primer hilo se despierta y empieza a tratar de
adquirir el candado otra vez.

Cuando el primer hilo logra obtener de nuevo el candado, contina desde el punto
en que se qued. Sin embargo, el hilo que estaba esperando puede no adquirir el
candado inmediatamente (o tal vez, nunca). Depende de cuando el segundo hilo
eventualmente libere el candado, y que hilo logre atraparlo enseguida. El primer
hilo no se despertar a menos que otro hilo llame al mtodo not i f y( ) . Hay una
versin sobrecargada de wai t ( ) , que permite especificar un tiempo de
expiracin. Si otro hilo no invoca al mtodo not i f y( ) en el tiempo especificado,
el hilo se despierta automticamente.

Para cada llamada a not i f y( ) , J ava despierta uno de los mtodos dormidos en
la llamada a wai t ( ) . Si hay varios mtodos esperando, J ava selecciona al primer
hilo en base a primero en entrar primero en salir.

La clase Obj ect tambin tiene el mtodo not i f yAl l ( ) que permite despertar a
todos los hilos esperando. En la mayora de los casos se desea llamar a
not i f yAl l ( ) en vez de not i f y( ) .




Tema 12 Hilos en Java 447
ITSON Manuel Domitsu Kono
Ejemplo sobre Esperar y Notificar

Considere el siguiente ejemplo. Se quiere simular el comportamiento de una cola a
la que llegan clientes a ser atendidos. Los clientes son atendidos en una o varias
estaciones. Para modelar la atencin que recibe el cliente se tiene la clase Tar ea:

Tarea.java
/ *
* Tar ea. j ava
*
* @aut hor mdomi t su
*/
package col a;

i mpor t j ava. ut i l . Dat e;

publ i c cl ass Tar ea {
i nt numTar ea;
Dat e t i meSt amp;
i nt dur aci on;

publ i c Tar ea( i nt numTar ea) {
t hi s. numTar ea = numTar ea;
t i meSt amp = new Dat e( ) ;
dur aci on = Gener ador Al eat or i o. get Poi sson( 1) ;
}

publ i c i nt get Dur aci on( ) {
r et ur n dur aci on;
}

publ i c i nt get NumTar ea( ) {
r et ur n numTar ea;
}

publ i c Dat e get Ti meSt amp( ) {
r et ur n t i meSt amp;
}

@Over r i de
publ i c St r i ng t oSt r i ng( ) {
r et ur n " Tar ea " + numTar ea + " : " + dur aci on + " , "
+ t i meSt amp. t oSt r i ng( ) ;
}
}

El atributo dur aci on de la clase Tar ea, modela el tiempo que el cliente tarda en
ser atendido. Para establecer ese tiempo se utiliza un generador de nmeros
aleatorios que tiene una distribucin de probabilidad de Poisson. Su cdigo se
encuentra en la clase Gener ador Al eat or i o.



448 Hilos en Java
ITSON Manuel Domitsu Kono
GeneradorAleatorio.java
/ *
* Gener ador Al eat or i o. j ava
*
* @aut hor mdomi t su
*/
package col a;

publ i c cl ass Gener ador Al eat or i o {
publ i c st at i c i nt get Poi sson( doubl e l ambda) {
doubl e L = Mat h. exp( - l ambda) ;
doubl e p = 1. 0;
i nt k = 0;

do {
k++;
p *= Mat h. r andom( ) ;
} whi l e ( p > L) ;

r et ur n k - 1;
}
}

La clase Col aTar eas representa la cola a la que llegan los clientes en espera de
ser atendidos. La cola tiene un tamao mximo de 5. Si la cola est llena, los
clientes deben esperan a que haya espacio para entrar a la cola.

ColaTareas.java
/ *
* Col aTar eas. j ava
*
* @aut hor mdomi t su
*/
package col a;

i mpor t j ava. ut i l . Ar r ayLi st ;
i mpor t j ava. ut i l . Li st ;

publ i c cl ass Col aTar eas {
pr i vat e Li st <Tar ea> col aTar eas = new Ar r ayLi st <Tar ea>( ) ;
pr i vat e st at i c f i nal i nt MAXQUEUE = 5;

publ i c synchr oni zed voi d ponTar ea( Tar ea t ar ea)
t hr ows I nt er r upt edExcept i on {

whi l e ( col aTar eas. si ze( ) == MAXQUEUE) {
wai t ( ) ;
}

col aTar eas. add( t ar ea) ;
not i f y( ) ;
}

publ i c synchr oni zed Tar ea obt enTar ea( ) t hr ows I nt er r upt edExcept i on {
not i f y( ) ;
Tema 12 Hilos en Java 449
ITSON Manuel Domitsu Kono
whi l e ( col aTar eas. si ze( ) == 0) {
wai t ( ) ;
}
Tar ea t ar ea = col aTar eas. get ( 0) ;
col aTar eas. r emove( t ar ea) ;

r et ur n t ar ea;
}

publ i c synchr oni zed i nt get Tamano( ) {
r et ur n col aTar eas. si ze( ) ;
}
}

Para modelar el arribo de los clientes se tiene el mtodo pr oduceTar ea( ) que
agrega un cliente a la cola. Para establecer ese tiempo entre arribos de los
clientes se utiliza un generador de nmeros aleatorios que tiene una distribucin
de probabilidad de Poisson. Los clientes llegan ms rpido de lo que pueden ser
atendidos.

Productor.java
/ *
* Pr oduct or . j ava
*
* @aut hor mdomi t su
*/
package hi l os;

i mpor t col a. Col aTar eas;
i mpor t col a. Gener ador Al eat or i o;
i mpor t col a. Tar ea;

publ i c cl ass Pr oduct or ext ends Thr ead {
pr i vat e st at i c f i nal i nt MAX_TAREAS = 10;
pr i vat e Col aTar eas col aTar eas;

publ i c Pr oduct or ( Col aTar eas col aTar eas) {
t hi s. col aTar eas = col aTar eas;
}

@Over r i de
publ i c voi d r un( ) {
i nt i = 0;
t r y {
whi l e ( i < MAX_TAREAS) {
pr oduceTar ea( i ) ;

/ / Si mul a el t i empo ent r e l l egadas
sl eep( 1000 * Gener ador Al eat or i o. get Poi sson( 0. 5) ) ;
i ++;
}
} cat ch ( I nt er r upt edExcept i on e) {
}
}

450 Hilos en Java
ITSON Manuel Domitsu Kono
pr i vat e voi d pr oduceTar ea( i nt i ) t hr ows I nt er r upt edExcept i on {
Tar ea t ar ea = new Tar ea( i ) ;

col aTar eas. ponTar ea( t ar ea) ;
Syst em. out . pr i nt l n( " Pr oduct or : " + t ar ea + " , "
+ col aTar eas. get Tamano( ) ) ;
}
}

Para modelar la atencin de los clientes se tiene la clase Consumi dor .

Consumidor.java
/ *
* Consumi dor . j ava
*
* @aut hor mdomi t su
*/
package hi l os;

i mpor t col a. Col aTar eas;
i mpor t col a. Tar ea;

publ i c cl ass Consumi dor ext ends Thr ead {
pr i vat e Col aTar eas col aTar eas;

publ i c Consumi dor ( St r i ng nombr e, Col aTar eas col aTar eas) {
super ( nombr e) ;
t hi s. col aTar eas = col aTar eas;
}

@Over r i de
publ i c voi d r un( ) {
t r y {
whi l e ( t r ue) {
Tar ea t ar ea = consumeTar ea( ) ;
sl eep( 1000 * t ar ea. get Dur aci on( ) ) ;
}
} cat ch ( I nt er r upt edExcept i on e) {}
}

publ i c Tar ea consumeTar ea( ) t hr ows I nt er r upt edExcept i on {
Tar ea t ar ea;

t ar ea = col aTar eas. obt enTar ea( ) ;
Syst em. out . pr i nt l n( get Name( ) + " : " + t ar ea+ " , "
+ col aTar eas. get Tamano( ) ) ;

r et ur n t ar ea;
}
}
En la siguiente clase de prueba se simula el caso de que hay una solo estacin de
atencin para los clientes:



Tema 12 Hilos en Java 451
ITSON Manuel Domitsu Kono
Prueba.java
/ *
* Pr ueba. j ava
*
* @aut hor mdomi t su
*/

package pr uebas;

i mpor t col a. Col aTar eas;
i mpor t hi l os. Consumi dor ;
i mpor t hi l os. Pr oduct or ;

/ **
*
* @aut hor mdomi t su
*/
publ i c cl ass Pr ueba {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Col aTar eas col aMensaj es = new Col aTar eas( ) ;

Pr oduct or pr oduct or = new Pr oduct or ( col aMensaj es) ;
Consumi dor consumi dor 1 = new Consumi dor ( " Consumi dor 1" ,
col aMensaj es) ;

pr oduct or . st ar t ( ) ;
consumi dor 1. st ar t ( ) ;
}
}

Lo siguiente es una corrida del programa anterior.

Pr oduct or : Tar ea 0: 1, Fr i Apr 29 00: 24: 37 MST 2011, 0
Pr oduct or : Tar ea 1: 3, Fr i Apr 29 00: 24: 37 MST 2011, 1
Pr oduct or : Tar ea 2: 0, Fr i Apr 29 00: 24: 37 MST 2011, 2
Pr oduct or : Tar ea 3: 0, Fr i Apr 29 00: 24: 37 MST 2011, 3
Consumi dor 1: Tar ea 0: 1, Fr i Apr 29 00: 24: 37 MST 2011, 3
Consumi dor 1: Tar ea 1: 3, Fr i Apr 29 00: 24: 37 MST 2011, 2
Pr oduct or : Tar ea 4: 1, Fr i Apr 29 00: 24: 39 MST 2011, 3
Pr oduct or : Tar ea 5: 1, Fr i Apr 29 00: 24: 40 MST 2011, 4
Pr oduct or : Tar ea 6: 0, Fr i Apr 29 00: 24: 40 MST 2011, 5
Consumi dor 1: Tar ea 2: 0, Fr i Apr 29 00: 24: 37 MST 2011, 4
Consumi dor 1: Tar ea 3: 0, Fr i Apr 29 00: 24: 37 MST 2011, 3
Consumi dor 1: Tar ea 4: 1, Fr i Apr 29 00: 24: 39 MST 2011, 2
Pr oduct or : Tar ea 7: 1, Fr i Apr 29 00: 24: 40 MST 2011, 3
Pr oduct or : Tar ea 8: 0, Fr i Apr 29 00: 24: 42 MST 2011, 3
Consumi dor 1: Tar ea 5: 1, Fr i Apr 29 00: 24: 40 MST 2011, 3
Consumi dor 1: Tar ea 6: 0, Fr i Apr 29 00: 24: 40 MST 2011, 3
Pr oduct or : Tar ea 9: 1, Fr i Apr 29 00: 24: 43 MST 2011, 3
Consumi dor 1: Tar ea 7: 1, Fr i Apr 29 00: 24: 40 MST 2011, 2
Consumi dor 1: Tar ea 8: 0, Fr i Apr 29 00: 24: 42 MST 2011, 1
Consumi dor 1: Tar ea 9: 1, Fr i Apr 29 00: 24: 43 MST 2011, 0

En la siguiente clase de prueba se simula el caso de que hay dos estaciones de
atencin para los clientes:
452 Hilos en Java
ITSON Manuel Domitsu Kono
Prueba.java
/ *
* Pr ueba. j ava
*
* @aut hor mdomi t su
*/

package pr uebas;

i mpor t col a. Col aTar eas;
i mpor t hi l os. Consumi dor ;
i mpor t hi l os. Pr oduct or ;

/ **
*
* @aut hor mdomi t su
*/
publ i c cl ass Pr ueba {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Col aTar eas col aMensaj es = new Col aTar eas( ) ;

Pr oduct or pr oduct or = new Pr oduct or ( col aMensaj es) ;
Consumi dor consumi dor 1 = new Consumi dor ( " Consumi dor 1" ,
col aMensaj es) ;
Consumi dor consumi dor 2 = new Consumi dor ( " Consumi dor 2" ,
col aMensaj es) ;

pr oduct or . st ar t ( ) ;
consumi dor 1. st ar t ( ) ;
consumi dor 2. st ar t ( ) ;
}
}

Lo siguiente es una corrida del programa anterior.

Pr oduct or : Tar ea 0: 1, Fr i Apr 29 00: 26: 58 MST 2011, 0
Consumi dor 2: Tar ea 0: 1, Fr i Apr 29 00: 26: 58 MST 2011, 0
Consumi dor 1: Tar ea 1: 4, Fr i Apr 29 00: 26: 59 MST 2011, 0
Pr oduct or : Tar ea 1: 4, Fr i Apr 29 00: 26: 59 MST 2011, 0
Pr oduct or : Tar ea 2: 2, Fr i Apr 29 00: 27: 00 MST 2011, 0
Consumi dor 2: Tar ea 2: 2, Fr i Apr 29 00: 27: 00 MST 2011, 0
Pr oduct or : Tar ea 3: 0, Fr i Apr 29 00: 27: 01 MST 2011, 1
Pr oduct or : Tar ea 4: 0, Fr i Apr 29 00: 27: 01 MST 2011, 2
Pr oduct or : Tar ea 5: 0, Fr i Apr 29 00: 27: 01 MST 2011, 3
Pr oduct or : Tar ea 6: 2, Fr i Apr 29 00: 27: 01 MST 2011, 4
Pr oduct or : Tar ea 7: 0, Fr i Apr 29 00: 27: 01 MST 2011, 5
Consumi dor 2: Tar ea 3: 0, Fr i Apr 29 00: 27: 01 MST 2011, 4
Consumi dor 2: Tar ea 4: 0, Fr i Apr 29 00: 27: 01 MST 2011, 3
Consumi dor 2: Tar ea 5: 0, Fr i Apr 29 00: 27: 01 MST 2011, 2
Consumi dor 2: Tar ea 6: 2, Fr i Apr 29 00: 27: 01 MST 2011, 1
Consumi dor 1: Tar ea 7: 0, Fr i Apr 29 00: 27: 01 MST 2011, 0
Pr oduct or : Tar ea 8: 2, Fr i Apr 29 00: 27: 03 MST 2011, 0
Pr oduct or : Tar ea 9: 0, Fr i Apr 29 00: 27: 03 MST 2011, 1
Consumi dor 1: Tar ea 8: 2, Fr i Apr 29 00: 27: 03 MST 2011, 0
Consumi dor 2: Tar ea 9: 0, Fr i Apr 29 00: 27: 03 MST 2011, 0

Das könnte Ihnen auch gefallen