Você está na página 1de 17

CAPTULO 2. PROCESOS VS.

HILOS

El objetivo de este captulo es el de discutir con un poco ms de profundidad


todo lo relacionado con el concepto de proceso que sea relevante para el
estudio de la programacin concurrente. En esta discusin aparecer un nuevo
concepto que es el de hilo o hebra1 y que resulta fundamental para entender
cmo es la programacin concurrente en Java.
En primer lugar veremos el ciclo de vida de un proceso y las nociones de
planificacin de procesos y cambio de contexto. Posteriormente, se ver la
disposicin en memoria de las estructuras de datos relacionadas con un
proceso. Por ltimo nos adentraremos en el mundo de los hilos y su relacin
con los procesos y, particularmente, nos centraremos en el estudio de la
gestin de hilos en Java.

2.1 Procesos
2.1.1 Ciclo de vida de un proceso
En la Figura 1 puede apreciarse el ciclo de vida que suele seguir un proceso.
Este ciclo de vida es prcticamente estndar en todos los SSOO.

Creado

es su turno
Listo Ejecucin Terminado
fin de turno o abandono
voluntario
espera suceso
ocurre suceso
Bloqueado

Figura 1 Estados de un proceso.

En un principio, un proceso no existe. En algn momento es creado. La


forma de crearlo variar en funcin del lenguaje que se est utilizando. Una vez
creado el proceso, ste pasa al estado denominado Listo. Este estado significa
que el proceso est en condiciones de hacer uso de la CPU en cuanto se le d
la oportunidad. El encargado de darle la oportunidad de usar la CPU es el
denominado planificador de procesos o scheduler, que suele formar parte
del SO. Como lo normal es que haya ms procesos que procesadores, no
todos los procesos que pueden hacer uso de la CPU en un momento
determinado pueden hacerlo realmente. Esos procesos permanecen en el

1
Suelen ser las traducciones ms aceptadas del trmino ingls thread. A lo largo de este libro
utilizaremos el trmino hilo. Usaremos Thread cuando hagamos referencia a la clase con ese nombre en
Java.

M.E Carmen Cern G. Programacin Concurrente BUAP 23


estado listo hasta que el planificador decide darles tiempo de CPU. Cuando el
planificador decide dar tiempo de CPU a un proceso, ste pasa al estado de
Ejecucin.

Como puede comprobarse en la Figura 1, un proceso tambin puede pasar


de Ejecucin a Listo. Esta decisin la toma el planificador. El planificador sigue
algn tipo de poltica de planificacin para asignar la CPU a los distintos
procesos. Una forma bastante justa y extendida de hacerlo es mediante la
asignacin de rodajas de tiempo a cada uno de los procesos, de tal forma que
cuando un proceso cumple su tiempo de permanencia en el procesador, ste
es desalojado y pasado al estado listo. En este estado esperar una nueva
oportunidad para pasar a Ejecucin. Tambin es posible que un proceso
abandone voluntariamente la CPU y pase de esta forma al estado listo.

Tambin en la Figura 1 puede apreciarse la presencia del estado Bloqueado.


Un proceso puede pasar de Ejecucin a Bloqueado cuando ha de esperar
porque ocurra un determinado evento o suceso. Ejemplos de eventos pueden
ser la espera por la terminacin de una operacin de Entrada/Salida, la espera
por la finalizacin de una tarea por parte de otro proceso, un bloqueo voluntario
durante un cierto perodo de tiempo, etc. Una vez que ocurre el evento por el
que se est esperando, el proceso pasa al estado Listo. Al acto de cambiar un
proceso de estado se le denomina cambio de contexto.

2.1.2 Disposicin en memoria de un proceso


En un Sistema Operativo tradicional, la memoria suele estar dividida en dos
partes: un espacio de usuario donde suele encontrarse la mayora de la
informacin relativa a los procesos de usuario y un espacio del ncleo donde
reside el cdigo y las estructuras de datos propios del SO.
cdigo del
pila y puntero de pila programa de
usuario y contador
de programa
proceso
datos globales
Espacio del usuario

bloque de control del proceso


Espacio del ncleo
ncleo

cdigo del SO

datos del SO

Figura 2 Mapa de memoria de un proceso para un SO multitarea.

Cuando estamos ante un SO multitarea, la informacin relativa a un proceso


suele estar dividida entre los dos espacios (Figura 2). En el espacio de usuario
se encuentra informacin propia del proceso tales como el cdigo del proceso,
el contador de programa, sus variables, su pila y su puntero de pila. Sin
embargo, el SO necesita tener informacin del estado de los procesos para
poder realizar apropiadamente los cambios de contexto. Esta informacin, que
suele conocerse con el nombre de bloque de control del proceso (PCB),

M.E Carmen Cern G. Programacin Concurrente BUAP 24


suele residir en el espacio del ncleo. En la Figura 3 puede verse la estructura
de un proceso en el SO Unix. La informacin contenida en la estructura del
proceso puede variar de un SO a otro, pero sustancialmente suele ser la
misma.

ID del proceso

UID, GID, EUID, ...

Tabla de seales

Mapa de memoria

prioridad

Descriptores de ficheros registros

pila

...

Estado de la CPU

Figura 3 Estructura tradicional de un proceso en UNIX.

Cuando tenemos ms de un proceso, se tiene algo como lo representado en


la Figura 4. En el espacio del ncleo estar el planificador de procesos que
ser el encargado de decidir cundo hacer los cambios de contexto. Cuando se
hace el cambio de contexto hay que recuperar la estructura del proceso que se
quiere poner en el estado Ejecucin y actualizar convenientemente los registros
del procesador para que el nuevo proceso tome el control del mismo. Los
cambios de contexto son costosos desde el punto de vista del tiempo de
ejecucin pues consumen un tiempo considerable.
Cada proceso de los representados en la figura tiene su propio contador de
programa, su propia pila, etc. Cada proceso suele tener un solo hilo de
ejecucin; se dice entonces que son monohilo.

espacio del usuario


proceso 1 proceso 2 proceso 3
espacio del ncleo

Figura 4 Varios procesos en un SO multitarea.

2.2 Hilos
Aunque el concepto de hilo lleva existiendo varias dcadas, no ha sido hasta
los 90 cuando ha alcanzado una cierta mayora de edad. Mientras que a
comienzos de los 90 el uso de hilos se circunscriba a la investigacin en
universidades y al desarrollo en sectores industriales especficos, a finales de
los 90 y en el nuevo milenio, el uso de hilos est ampliamente extendido. La

M.E Carmen Cern G. Programacin Concurrente BUAP 25


incorporacin del concepto de hilo al lenguaje Java ha supuesto un empuje
definitivo a su uso.
Pero, qu es un hilo?. De la misma manera que un Sistema Operativo
puede ejecutar varios procesos al mismo tiempo bien sea por concurrencia o
paralelismo, dentro de un proceso puede haber varios hilos de ejecucin. Por
tanto, un hilo puede definirse como cada secuencia de control dentro de
un proceso que ejecuta sus instrucciones de forma independiente.
En la Figura 5 puede verse cmo sobre el hardware subyacente (una o
varias CPUs) se sita el Sistema Operativo. Sobre ste se sitan los procesos
(Pi) que pueden ejecutarse concurrentemente y dentro de estos se ejecutan los
hilos (hj) que tambin se pueden ejecutar de forma concurrente dentro del
proceso. Es decir, tenemos concurrencia a dos niveles, una entre procesos y
otra entre hilos de un mismo proceso. Si por ejemplo tenemos dos
procesadores, se podran estar ejecutando al mismo tiempo el hilo 1 del
proceso 1 y el hilo 2 del proceso 3. Otra posibilidad podra ser el hilo 1 y el hilo
2 del proceso 1.

procesos hilos
...

espacio de usuario

t1 h2 h1 h2 h3 h4 h1 h2 h3
PCB PCB PCB

espacio del P1 P2 P3
ncleo

hilos del sistema Sistema Operativo

CPU o conjunto de CPUs

Figura 5 Concurrencia a dos niveles: procesos e hilos.

Los procesos son entidades pesadas. La estructura del proceso est en la


parte del ncleo y, cada vez que el proceso quiere acceder a ella, tiene que
hacer algn tipo de llamada al sistema, consumiendo tiempo extra de
procesador. Por otra parte, los cambios de contexto entre procesos son
costosos en cuanto a tiempo de computacin se refiere. Por el contrario, la
estructura de los hilos reside en el espacio de usuario, con lo que un hilo es
una entidad ligera. Los hilos comparten la informacin del proceso (cdigo,
datos, etc). Si un hilo modifica una variable del proceso, el resto de hilos vern
esa modificacin cuando accedan a esa variable. Los cambios de contexto
entre hilos consumen poco tiempo de procesador, de ah su xito.
Podemos hablar de dos niveles de hilos: aquellos que nosotros usaremos
para programar y que pueden crearse desde lenguajes de programacin como
Java y aquellos otros hilos del propio SO que sirven para dar soporte a
nuestros hilos de usuario y que son los hilos del sistema segn la Figura 5.
Cuando nosotros programamos con hilos, hacemos uso de un API (Application

M.E Carmen Cern G. Programacin Concurrente BUAP 26


Program Interface) proporcionado por el lenguaje, el SO o un tercero mediante
una librera externa. El implementador de este API ser el que haya usado los
hilos del sistema para dar soporte de ejecucin a los hilos que nosotros
creamos en nuestros programas. En la Figura 6 puede observarse este hecho
aplicado a Java.
Nuestro inters radica en aquellos hilos del espacio de usuario, es decir,
aquellos hilos que uno crea para la construccin de sus programas. Sin
embargo, aunque no es el objetivo principal de este libro, nos adentraremos un
poco en la forma en la que pueden ser implementados los hilos del sistema
para entender ciertas cosas que ocurren con los hilos de Java, pues el
comportamiento de stos a veces depende de la forma en la que se gestione la
librera de hilos del sistema.
programador de
aplicaciones

usa

API de hilos de Java


programador de la
librera de hilos de la
usa MVJ

hilos del sistema

Figura 6 Dos niveles de hilos.

2.2.1 Estndares de hilos


Cada SO implementa los hilos del sistema como quiere, aunque se puede
hablar de la existencia de tres estndares. Existen tres libreras nativas
diferentes de hilos que compiten hoy en da para ser las ms usadas: Win32,
OS/2 y POSIX. Las dos primeras son propietarias y slo corren bajo sus
respectivas plataformas (NT, Win95, OS/2). La especificacin POSIX (IEEE
1003.1c) conocida como pthreads [Butenhof, 1997] est pensada para todas
las plataformas y est disponible para la mayora de las implementaciones
UNIX y Linux, as como VMS y AS/400.
Por su parte, los hilos de Java estn implementados en la MVJ (Mquina
Virtual Java) que a su vez est construida sobre la librera de hilos nativas de la
correspondiente plataforma (ver Figura 6). Java slo ofrece su API para
manejar hilos, independientemente de la librera subyacente. De esta forma es
mucho ms fcil utilizar las hilos de Java. Sin embargo, y como se ver
posteriormente, hay algunos asuntos importantes a tener en cuenta para que
un programa multihilo en Java sea independiente de la plataforma.

2.2.2 Implementacin de hilos


Hay fundamentalmente 2 formas de implementar hilos. La primera es escribir
una librera al nivel de usuario. Todas las estructuras y el cdigo de la librera
estarn en espacio de usuario. La mayora de las llamadas que se hagan
desde la librera se ejecutarn en el espacio de usuario y no har ms uso de
las rutinas del sistema que cualquier otra librera o programa de usuario. La

M.E Carmen Cern G. Programacin Concurrente BUAP 27


segunda forma es una implementacin al nivel de ncleo. La mayora de las
llamadas de la librera requerirn llamadas al sistema.
Algunas de las implementaciones del estndar POSIX son del primer tipo,
mientras que tanto OS/2 como Win32 son del segundo tipo. Ambos mtodos
pueden ser usados para implementar exactamente el mismo API. En el caso de
Java, el implementador de la MVJ es el que tendr que pelearse con el API de
esa librera. El programador de aplicaciones usar el API implementado por la
librera de hilos de la MVJ. Al programador de aplicaciones le debe dar igual
cmo est implementada la librera de la MVJ.
Para una discusin ms a fondo de la implementacin de hilos se sugiere
consultar [Lewis and Berg, 2000].

2.2.3 Planificacin de hilos


Los SSOO modernos como Solaris definen el concepto de procesador lgico
de tal forma que donde se ejecutan los hilos de usuario es en un procesador
lgico. En la terminologa de Solaris a estos procesadores lgicos se les
denomina LWP (Light-Weight Processes)
De esta forma vamos a tener una planificacin a dos niveles. Un primer nivel
para asignar los hilos de usuario a los procesadores lgicos y otro para asignar
los procesadores lgicos al procesador o procesadores fsicos. En la Figura 7
pueden observarse estos dos niveles segn la filosofa de Solaris. Los hilos de
usuario compiten por los procesadores lgicos (primer nivel de planificacin)
mientras que estos a su vez competirn por los hilos del sistema que son los
que directamente se ejecutarn en los procesadores fsicos (segundo nivel de
planificacin)
Hay principalmente tres tcnicas distintas para hacer la planificacin de hilos
sobre los recursos del ncleo (indirectamente sobre las distintas CPUs). Se
describen a continuacin.

Proceso 1 Proceso 2 Proceso 3 Proceso 4

espacio de usuario
er
1 nivel de
planificacin

LWP LWP LWP LWP LWP LWP LWP LWP

espacio del ncleo

hilos del sistema

2 nivel de planificacin

hardware

P1 P2 P3 P4

Figura 7 Planificacin de procesos e hilos en Solaris.

M.E Carmen Cern G. Programacin Concurrente BUAP 28


Muchos hilos en un procesador lgico
Conocido como el modelo muchos-a-uno. Todos los hilos creados en el
espacio de usuario hacen turnos para ir ejecutndose en el nico procesador
lgico asignado a ese proceso. De esta forma un proceso no toma ventaja de
una mquina con varios procesadores fsicos. Otra desventaja es que cuando
se hace una llamada bloqueante, p. ej. de E/S, todo el proceso se bloquea. Es
algo parecido a lo que ocurra con los procesos en Pascal-FC pero aqu al nivel
de hilo. Como ventaja podemos decir que en este modelo la creacin de hilos y
la planificacin se hacen en el espacio de usuario al 100%, sin usar los
recursos del ncleo. Por tanto es ms rpido. Sera el caso del proceso 1 en la
Figura 7.

Un hilo por procesador lgico


Tambin llamado como modelo uno-a-uno. Aqu se asigna un procesador
lgico para cada hilo de usuario. Este modelo permite que muchos hilos se
ejecuten simultneamente en diferentes procesadores fsicos. Sin embargo,
tiene el inconveniente de que la creacin de hilos conlleva la creacin de un
procesador lgico y por tanto una llamada al sistema. Como cada procesador
lgico toma recursos adicionales del ncleo, uno est limitado en el nmero de
hilos que puede crear. Win32 y OS/2 utilizan este modelo. Algunas
implementaciones de POSIX como los LinuxThreads de Xavier Leroy tambin
lo usan. Cualquier MVJ basada en estas libreras tambin usa este modelo, as
pues Java sobre Win32 lo usa. Sera el caso del proceso 2 en la Figura 7.

Muchos hilos en muchos procesadores lgicos


Llamado modelo muchos-a-muchos (estricto). Un nmero de hilos es
multiplexado en un nmero de procesadores lgicos igual o menor. Numerosos
hilos pueden correr en paralelo en diferentes CPUs y las llamadas al sistema
de tipo bloqueante no bloquean al proceso entero. Sera el caso del proceso 3
en la Figura 7.

El modelo de dos niveles


Llamado modelo muchos-a-muchos (no estricto). Es como el anterior pero
ofrece la posibilidad de hacer un enlace de un hilo especfico con un
procesador lgico. Probablemente sea el mejor modelo. Varios sistemas
operativos usan este modelo (Digital UNIX, Solaris, IRIX, HP-UX, AIX). En el
caso de Java, las MVJ sobre estos SSOO tienen la opcin de usar cualquier
combinacin de hilos enlazadas o no. La eleccin del modelo de hilos es una
decisin al nivel de implementacin de los escritores de la MVJ. Java en s
mismo no tiene el concepto de procesador lgico. Sera el caso del proceso 4
en la Figura 7.

Con esto daramos por terminada la discusin sobre hilos en general y nos
adentramos en el mundo de los hilos en Java, es decir, en el mundo de los
hilos desde una perspectiva de programador de aplicaciones y no de Sistema
Operativo.

M.E Carmen Cern G. Programacin Concurrente BUAP 29


2.3 Hilos en java
Java proporciona un API para el uso de hilos. Este API es bastante simple
(aproximadamente una veintena de mtodos) comparado con otras libreras de
hilos como Posix que alcanza el medio centenar de mtodos.
Como prcticamente todo en Java, los hilos se representan mediante una
clase, la clase Thread. Esta clase se encuentra en el paquete java.lang.Thread. Los
mtodos de esta clase junto con algunos mtodos de la clase Object son los que
permiten un manejo prcticamente completo de hilos en Java.
En esta primera incursin en el mundo de los hilos en Java veremos la
diferencia entre hilos y objetos, las dos formas distintas que hay de crear hilos
en Java, el ciclo de vida de un hilo y las prioridades de los hilos.

2.3.1 Hilos y objetos


Nada ms empezar un programa en Java, existe un hilo de ejecucin que es el
indicado por el mtodo main, es el denominado hilo principal. Si no se crean
ms hilos desde el hilo principal tendremos algo como lo representado en la
Figura 8 donde slo existe un hilo de ejecucin que va recorriendo los distintos
objetos participantes en el programa segn se vayan produciendo las distintas
llamadas entre mtodos de los mismos.
A D G

B H
E

main()
F
C

Figura 8 Varios objetos y un solo hilo.

Sin embargo, si desde el hilo principal se crean por ejemplo dos hilos
tendremos algo como lo representado en la Figura 9, en la que se puede ver
cmo el programa se est ejecutando al mismo tiempo por tres sitios distintos:
el hilo principal ms los dos hilos creados. Los hilos pueden estar ejecutando
cdigo en diferentes objetos, cdigo diferente en el mismo objeto o incluso el
mismo cdigo en el mismo objeto y al mismo tiempo.
Es necesario en este punto ver la diferencia entre objeto e hilo. Un objeto es
algo esttico, tiene una serie de atributos y mtodos. Quien realmente ejecuta
esos mtodos es el hilo de ejecucin. En ausencia de hilos slo hay un hilo que
va recorriendo los objetos segn se van produciendo las llamadas entre
mtodos de los objetos. Podra darse el caso del objeto E en la Figura 9, donde
es posible que tres hilos de ejecucin distintos estn ejecutando el mismo
mtodo al mismo tiempo.

M.E Carmen Cern G. Programacin Concurrente BUAP 30


A D G

B H
E
main()

C F

Figura 9 Tres hilos atravesando varios objetos.

2.3.2 Creacin de hilos


Existen 2 formas de trabajar con hilos en cuanto a su creacin se refiere:
- Heredando de la clase Thread
- Implementando la interfaz Runnable

Heredando de Thread y redefiniendo el mtodo run


La clase Thread tiene un mtodo especial cuya signatura es public void run ().
Cuando queramos crear una clase cuyas instancias queremos que sean un hilo
tendremos que heredar de la clase Thread y redefinir este mtodo. Dentro de
este mtodo se escribe el cdigo que queremos que se ejecute cuando se
lance a ejecucin el hilo. Se podra decir que main() es a un programa lo que
run() es a un hilo.
Veamos un ejemplo en el que creamos una clase denominada
ThreadConHerencia. Si queremos que los objetos pertenecientes a esta clase
sean hilos entonces debemos extender la clase Thread. El constructor de la
clase ThreadConHerencia recibe como parmetro una palabra a imprimir. En el
mtodo run() especificamos el cdigo que ha de ejecutarse al lanzar el hilo: la
impresin de la palabra 10 veces.

class ThreadConHerencia extends Thread {


String palabra;

public ThreadConHerencia (String _palabra) {


palabra = _palabra;
}

public void run( ) {


for (int i=0; i<10; i++)
System.out.print (palabra);
}

Hasta aqu lo nico que hemos hecho es crear una clase. Los objetos
pertenecientes a esa clase sern hilos. En las siguientes lneas, desde el
programa principal creamos dos objetos que son hilos, a y b.

M.E Carmen Cern G. Programacin Concurrente BUAP 31


public static void main(String[] args) {
Thread a = new ThreadConHerencia ("hiloUno");
Thread b = new ThreadConHerencia ("hiloDos");

Hasta aqu los hilos estn creados, pero no han sido puestos en ejecucin.
Para ponerlos en ejecucin hay que invocar el mtodo start(). Este mtodo
pertenece a la clase Thread. Se encarga de hacer algunas inicializaciones
propias del hilo y posteriormente invoca al mtodo run(). Es decir, invocando
start, se ejecutan en orden los mtodos start (heredado) y run (redefinido)
Las lneas siguientes habra que aadirlas al programa principal y ya
tendramos nuestro primer programa con hilos en Java.

a.start();
b.start();
System.out.println (Fin del hilo principal)
}

Una posible salida para este programa sera:


hiloDos hiloDos hiloUno hiloDos Fin del Hilo principal hiloUno hiloUno hiloUno
hiloUno hiloUno hiloDos hiloDos hiloDos hiloDos hiloUno hiloUno hiloUno hiloUno
hiloDos hiloDos hiloDos

Puede apreciarse cmo se intercalan las salidas de los tres hilos.


Recordemos que tenemos tres hilos: el principal ms los dos creados. Y entre
ellos se pueden intercarlar de cualquier forma, por tanto no es extrao ver
cmo el hilo principal acaba antes que los otros o cmo el hilo dos comienza
antes que el uno.

Implementando la interfaz Runnable


Volveremos a implementar el mismo ejemplo pero usando esta otra forma de
crear hilos. En las siguientes lneas de cdigo creamos una clase denominada
ThreadConRunnable. Ahora, en vez de heredar de la clase Thread, implementamos
la interfaz Runnable. Esta interfaz slo tiene un mtodo con la signatura public void
run(). Este mtodo es el que, como mnimo, tenemos que implementar en esta
clase.

public class ThreadConRunnable implements Runnable {


String palabra;

public ThreadConRunnable (String palabra {


palabra = _palabra;
}

public void run() {


for (int i = 0;i<10;i++) {
System.out.println (palabra);
}

Hasta aqu simplemente hemos creado una clase. Al contrario que antes, los
objetos de esta clase no sern hilos pues no hemos heredado de la clase

M.E Carmen Cern G. Programacin Concurrente BUAP 32


Thread. Si queremos que un objeto de la clase ThreadConRunnable se ejecute en
un hilo independiente, entonces tenemos que crear un objeto de la clase Thread
y pasarle como parmetro el objeto donde queremos que empiece su ejecucin
ese hilo. En las siguientes lneas de cdigo se crean dos objetos de la clase
ThreadConRunnable (a y b) y dos hilos (t1 y t2) a los que se le pasa como
parmetro los objetos a y b. Posteriormente hay que invocar el mtodo start() de
la clase Thread que ya se encarga de invocar al mtodo run() de los objetos a y b
respectivamente.

public static void main (String args[]) {


ThreadConRunnable a = new ThreadConRunnable ("hiloUno");
ThreadConRunnable b = new ThreadConRunnable ("hiloDos");
Thread t1 = new Thread (a);
Thread t2 = new Thread (b);
t1.start();
t2.start();
System.out.println (Fin del hilo principal)
}
}

Una posible salida de este ejemplo sera la misma de antes.

Comparando ambos mtodos


La segunda forma de crear hilos puede parecer un poco ms confusa. Sin
embargo, es ms apropiada y se utiliza ms que la primera. Esto se debe a que
como en Java no hay herencia mltiple, el utilizar la primera opcin hipoteca
nuestra clase para que ya no pueda heredar de otras clases. Sin embargo, con
la segunda opcin puedo hacer que el mtodo run() de una clase se ejecute en
un hilo y adems esta clase herede el comportamiento de otra clase. En la
medida de lo posible, deberamos usar esta forma.

2.3.3 Objeto autnomo en un hilo


Independientemente del mtodo que usemos para la creacin de hilos, se
puede apreciar cmo el hecho de que el objeto que implementa el mtodo run
se ejecute en un hilo depende del mtodo cliente, en nuestro caso del mtodo
main. Es decir, el cliente, en nuestro caso el programa principal, tiene que
lanzar a ejecucin el nuevo hilo. Incluso en el caso de la implementacin de la
interfaz Runnable tambin tiene que crearlo explcitamente. Puede que a veces
sea esto lo que queramos, que el cliente tenga el control de la creacin y
lanzamiento de hilos. Sin embargo, a veces es preferible un objeto autnomo
que automticamente se ejecute en un nuevo hilo, sin intervencin del cliente.
No en vano, esto es lo que ocurre con algunos objetos de Java. Sin nuestra
intervencin se ejecutan en un hilo independiente.
Veamos un ejemplo en el que al construir un objeto ste se ejecuta
automticamente en un nuevo hilo. Para ello, en el constructor creamos el hilo
y lo lanzamos.

class AutoThread implements Runnable {


private Thread me_;

M.E Carmen Cern G. Programacin Concurrente BUAP 33


public AutoThread() {
me_ = new Thread(this);
me_.start();
}

public void run() {


if (me_==Thread.currentThread())
for (int i=0;i<10;i++)
System.out.println ("Dentro de Autothread.run()");
}

public static void main(String[] args) {


AutoThread miThread = new AutoThread();
for (int i = 0;i<10;i++)
System.out.println ("Dentro de main()");
}

Como vemos, en la implementacin del mtodo run() hay que controlar quin
es el hilo que se est ejecutando. Para ello nos servimos del mtodo
currentThread() de la clase Thread. Este mtodo nos devuelve una referencia al
hilo que est ejecutando ese cdigo. Esto se hace para evitar que cualquier
mtodo de un hilo distinto haga una llamada a run() directamente, como ocurre
en el siguiente caso.

public static void main (String[] args) {


AutoThread miThread = new AutoThread();
miThread.run();
while (true)
System.out.println (Dentro del main());
}
}

2.3.4 Estados de un hilo en Java


El ciclo de vida de un hilo comprende diversos estados por los que puede ir
pasando. La Figura 10 muestra estos estados y los mtodos que provocan el
paso de un estado a otro.

M.E Carmen Cern G. Programacin Concurrente BUAP 34


nuevo listo

planificador selecciona hilo


expiracin de cuanto o yield()

ejecucin

terminada la operacin de E/S


por operacin de E/S bloqueado

por ejecucin del pasado el intervalo especificado


mtodo sleep () dormido

por intento de adquirir el espera por por adquisicin del cerrojo del objeto
cerrojo de un objeto
synchronized

por ejecucin del espera por por ejecucin de notify () o notifyAll ()


mtodo wait ()
wait

terminado

Figura 10 Estados de un hilo en Java.


Nuevo es el estado en el que se encuentra un hilo cuando se crea con el
operador new. Al lanzarlo con start() pasa al estado listo, es decir, es susceptible
de acaparar el procesador si el planificador se lo permite. Cuando obtiene el
procesador pasa al estado ejecucin. Una vez que el hilo est en ejecucin
puede pararse por diversos motivos:
Por hacer una operacin de E/S. Saldr de este estado al
completarse dicha operacin.
Por ejecutar el mtodo sleep (milisegundos). Saldr de este estado al
cumplirse el tiempo especificado como parmetro.
Por intentar adquirir el cerrojo de un objeto. Hablaremos del cerrojo
de un objeto en el captulo 4.
Por ejecutar el mtodo wait(). Saldr de este estado cuando se
ejecute el mtodo notify() o notifyAll() por parte de otro hilo. Hablaremos
de esto en el captulo 4.

Como puede apreciarse, el ciclo de vida de un hilo es muy parecido al ciclo


de vida de un proceso. El planificador de los hilos de nuestras aplicaciones
Java se encuentra implementado en la MVJ. A continuacin hacemos algunas
consideraciones sobre cmo se hace esa planificacin en Java, es decir, cmo
es el paso del estado listo a ejecucin y viceversa. Los dos estados
sombreados sern tratados con profundidad en el tema 4.

M.E Carmen Cern G. Programacin Concurrente BUAP 35


2.3.5 Planificacin y prioridades
Java fue diseado principalmente para obtener una gran portabilidad. Este
objetivo hipoteca de alguna forma tambin el modelo de hilos a usar en Java
pues se deseaba que fuera fcil de soportar en cualquier plataforma y, como se
ha visto, cada plataforma hace las cosas de una forma diferente. Esto hace que
el modelo de hilos sea demasiado generalista. Sus principales caractersticas
son:
Todos los hilos de Java tienen una prioridad y se supone que el
planificador dar preferencia a aquel hilo que tenga una prioridad ms
alta. Sin embargo, no hay ningn tipo de garanta de que en un
momento determinado el hilo de mayor prioridad se est ejecutando.
Las rodajas de tiempo pueden ser aplicadas o no. Depender de la
gestin de hilos que haga la librera sobre el que se implementa la
mquina virtual java.

Ante una definicin tan vaga como esta uno se puede preguntar cmo es
posible escribir cdigo portable y, lo que es peor, el comportamiento que tendr
la aplicacin dependiendo del sistema subyacente. Ante esto, lo que el
programador no debe hacer es ningn tipo de suposicin y ponerse siempre en
el peor de los casos:
Se debe asumir que los hilos pueden intercalarse en cualquier punto en
cualquier momento.
Nuestros programas no deben estar basados en la suposicin de que
vaya a haber un intercalado entre los hilos. Si esto es un requisito en
nuestra aplicacin, deberemos introducir el cdigo necesario para que
esto sea as.

Prioridades
Las prioridades de cada hilo en Java estn en el rango de 1 (MIN_PRIORITY) a
10 (MAX_PRIORITY). La prioridad de un hilo es inicialmente la misma que la
del hilo que lo cre. Por defecto todo hilo tiene la prioridad 5
(NORM_PRIORITY). El planificador siempre pondr en ejecucin aquel hilo con
mayor prioridad (teniendo en cuenta lo dicho en el punto anterior) Los hilos de
prioridad inferior se ejecutarn cuando estn bloqueados los de prioridad
superior.
Las prioridades se pueden cambiar utilizando el mtodo setPriority
(nuevaPrioridad). La prioridad de un hilo en ejecucin se puede cambiar en
cualquier momento. El mtodo getPriority() devuelve la prioridad de un hilo.
El mtodo yield()hace que el hilo actualmente en ejecucin ceda el paso de
modo que puedan ejecutarse otros hilos listos para ejecucin. El hilo elegido
puede ser incluso el mismo que ha dejado paso, si es el de mayor prioridad.
En el siguiente programa se puede ver cmo se crean dos hilos (t1 y t2) y al
primero de ellos se le cambia la prioridad. Esto hara que t2 acaparase el
procesador hasta finalizar pues nunca ser interrumpido por un hilo de menor
prioridad.

public class A implements Runnable {

String palabra;

M.E Carmen Cern G. Programacin Concurrente BUAP 36


public A (String _palabra) {
palabra = _palabra;
}

public void run () {


for (int i=0;i<100;i++)
System.out.println (palabra);
}

public static void main (String args[]) {


A a1 = new A("a1");
A a2 = new A("a2");
Thread t1 = new Thread (a1);
Thread t2 = new Thread (a2);
t1.start();
t1.setPriority(1);
System.out.println ("Prioridad de t1: "+t1.getPriority());
t2.start();
System.out.println ("Prioridad de t2: "+t2.getPriority());
}
}

Hilos daemon
Antes de lanzar un hilo, ste puede ser definido como un Daemon, indicando
que va a hacer una ejecucin continua para la aplicacin como tarea de fondo.
Entonces la mquina virtual abandonar la ejecucin cuando todos los hilos
que no sean Daemon hayan finalizado su ejecucin. Los hilos Daemon tienen
la prioridad ms baja. Se usa el mtodo setDaemon(true) para marcar un hilo
como hilo demonio y se usa getDaemon() para comprobar ese indicador. Por
defecto, la cualidad de demonio se hereda desde el hilo que crea el nuevo hilo.
No puede cambiarse despus de haber iniciado un hilo.
La propia MVJ pone en ejecucin algunos hilos daemon cuando ejecutamos
un programa. Entre ellos cabe destacar el garbage collector o recolector de
basura, que es el encargado de liberar la memoria ocupada por objetos que ya
no estn siendo referenciados.

2.3.6 La clase Thread


A continuacin mostramos los atributos y mtodos de la clase Thread tratados
en este captulo y algunos ms que consideramos de inters. No pretendemos
dar un listado exhaustivo con el API completo. En el captulo 4 se vern
algunos mtodos ms, no de la clase Thread, pero s relacionados con ella.

Atributos
public static final int MIN_PRIORITY
La prioridad mnima que un hilo puede tener.

public static final int NORM_PRIORITY


La prioridad por defecto que se le asigna a un hilo.

public static final int MAX_PRIORITY

M.E Carmen Cern G. Programacin Concurrente BUAP 37


La prioridad mxima que un hilo puede tener.

Constructores
public Thread ()
Crea un nuevo objeto Thread. Este constructor tiene el
mismo efecto que Thread (null, null, gname), donde gname
es un nombre generado automticamente y que tiene la
forma Thread-+n, donde n es un entero asignado
consecutivamente.
public Thread (String name)
Crea un nuevo objeto Thread, asignndole el nombre
name.
public Thread (Runnable target)
Crea un nuevo objeto Thread. target es el objeto que
contiene el mtodo run() que ser invocado al lanzar el
hilo con start().
public Thread (Runnable target, String name)
Crea un nuevo objeto Thread, asignndole el nombre
name. target es el objeto que contiene el mtodo run()
que ser invocado al lanzar el hilo con start().

Mtodos
public static Thread currentThread ()
Retorna una referencia al hilo que se est ejecutando
actualmente.
public static void dumpStack ()
Imprime una traza del hilo actual. Usado slo con
propsitos de depuracin..
public String getName ()
Retorna el nombre del hilo.
int getPriority ()
Retorna la prioridad del hilo.
public final boolean isAlive ()
Chequea si el hilo est vivo. Un hilo est vivo si ha sido
lanzado con start y no ha muerto todava.
public final void isDaemon ()
Devuelve verdadero si el hilo es daemon.
public final void join () throws InterruptedException
Espera a que este hilo muera.
public final void join (long millis) throws InterruptedException
Espera como mucho millis milisegundos para que este
hilo muera.
public final void join (long millis, int nanos) throws InterruptedException
Permite afinar con los nanosegundos nanos el tiempo a
esperar.
public void run ()
Si este hilo fue construido usando un objeto que
implementaba Runnable, entonces el mtodo run de ese
M.E Carmen Cern G. Programacin Concurrente BUAP 38
objeto es llamado. En cualquier otro caso este mtodo
no hace nada y retorna.
public final void setDaemon (boolean on)
Marca este hilo como daemon si el parmetro on es
verdadero o como hilo de usuario si es falso. El mtodo
debe ser llamado antes de que el hilo sea lanzado.
public final void setName (String name)
Cambia el nombre del hilo por name.
public final void setPriority (int newPriority)
Asigna la prioridad newPriority a este hilo.
public static void sleep (long millis) throws InterruptedException
Hace que el hilo que se est ejecutando actualmente
cese su ejecucin por los milisegundos especificados en
millis. Pasa al estado dormido. El hilo no pierde la
propiedad de ningn cerrojo que tuviera adquirido con
synchronized.
public static void sleep (long millis, int nanos) throws InterruptedException
Permite afinar con los nanosegundos nanos el tiempo a
estar dormido.
public void start ()
Hace que este hilo comience su ejecucin. La MVJ
llamar al mtodo run de este hilo.
public String toString ()
Devuelve una representacin en forma de cadena de
este hilo, incluyendo su nombre, su prioridad y su grupo.
public static void yield ()
Hace que el hilo que se est ejecutando actualmente
pase al estado listo, permitiendo a otro hilo ganar el
procesador.

2.4 Resumen
En este captulo hemos visto los conceptos fundamentales sobre procesos e
hilos que necesitamos desde el punto de vista de la programacin concurrente.
Con respecto a los procesos, hemos visto su ciclo de vida y su disposicin en
memoria, adentrndonos en el modelo de procesos de Pascal-FC y la gestin y
planificacin de procesos que hace. Con respecto a los hilos, hemos visto los
diferentes estndares que se pueden encontrar (Win32, OS/2 y posix), dos
posibles implementaciones (a nivel de usuario o a nivel de ncleo) y cmo es la
planificacin de hilos en un SO moderno como Solaris. Posteriormente nos
hemos adentrado en el mundo de los hilos en Java. Hemos visto la diferencia
entre hilo y objeto, diferentes formas de crear hilos, el ciclo de vida de un hilo y
su planificacin y prioridades. Finalmente se han mostrado los mtodos ms
usados e importantes de la clase Thread de Java.
Con todo lo visto en este captulo y en el anterior ya estamos en disposicin
de analizar en mayor profundidad los problemas inherentes a la programacin
concurrente y sus posibles soluciones utilizando distintas primitivas de
sincronizacin.

M.E Carmen Cern G. Programacin Concurrente BUAP 39

Você também pode gostar