Está en la página 1de 28

Programacin de Hilos 12 - 1

CAPITULO

12

Programacin de Hilos
Objetivos

Utilizar la clase Timer para manejo de procesos que requieran


ser controlados segn intervalos de tiempo dados.

Caracterizar aplicaciones que manejen multiples tareas

Desarrollar aplicaciones que permitan manejar animaciones.

Utilizar la clase Thread y la interface Runnable para el manejo


de hilos desde un programa.

Indicar los elementos necesarios para el manejo de la


sincronizacin de procesos

Introduccin
En la naturaleza se presentan innumerables ejemplos de procesos que se
realizan de manera parela, simultneos o concurrentes. Por ejemplo, para el
caso del lector, los procesos que est realizando todo su cuerpo integral,
mientras realiza la accin consciente, de leer este prrafo. En el plano de los
sistemas artificiales, la concurrencia es un denominador comn, el computador,
la internet, los juegos, en los sistemas sofisticados de los automviles
modernos, las actuales comunicaciones inalambricas, etc.
Por tales razones, la concurrencia es un fenmeno importante de tratar, ya que
permite el desarrollo de sistemas capaces de adelantar, mltiples procesos
simultneamente. A su vez cada proceso se puede componer de mltiples

Programacin de Hilos: Introduccin 12 - 2


tareas, ejecutndose segn ciertas reglas, de manera simultnea. Debe
entenderse que, se hace referencia a procesos en el sentido de programas, y
dentro de cualquier programa, la posibilidad de manejar, concurrentemente,
mltiples tareas.
Hasta el momento se han desarrollado programas que realizan una sola tarea,
entendiendose por tarea, un hilo de ejecucin o un thread, tambien
denominados de flujo nico, en contraste con los programas que realizan ms
de una tarea, o de multitareas o multithreads, denominados de flujo mltiple.
Especificamente una tarea se encarga de controlar un nico aspecto, dentro de la
ejecucin de un programa, por ejemplo el manejo de grficos, las
entradas/salidas de archivos en disco, u otros. Las tareas se diferencian de los
procesos, en que las primeras comparten los mismos recursos del programa que
las contiene, en tanto los procesos tienen en forma separada su cdigo, as
como sus datos.
Flujo nico: un programa de flujo nico (single-Thread) utiliza un solo
hilo para controlar su ejecucin. Por defecto, cualquier programa applet o
aplicacin Java, tiene un hilo denominado principal (Thread). En estos
programas este hilo no necesita explicitarse, pues por defecto son de flujo
nico. Realizan las operaciones indicadas en un solo hilo o nica tarea y
terminan. En otros terminos, un programa en ejecucin en Java es un hilo.
Flujo mltiple: un programa de flujo mltiple o multitarea, utiliza varios
contextos de ejecucin para realizar su trabajo. Cada tarea se inicia y
termina tan pronto como sea posible, lo cual es una facilidad para la
entrada de datos en sistemas en tiempo real, especialmente si estos son de
diferentes fuentes. En este caso, se dice que ademas del hilo principal del
programa en ejeccin, este tiene otros hilos, o tareas parelas en ejecucin.
Se puede definir hilo como una secuencia nica de control de flujo dentro de un
programa. Entendiendo que puede haber ms de una secuencia de control o
hilos. Java permite realizar programacin multihilo, el cual consiste en
programas que contienen dos o ms partes que se ejecutan de manera
concurrente. Una parte que se ejecuta independientemente de las dems se
denomina un hilo (thread).
En un entorno de multitarea basada en hilos, el hilo es la unidad de cdigo ms
pequea que se puede seleccionar para ejecucin. La programacin basada en
multihilos, permite escribir programas ms eficientes ya que optimizan los
recursos de la Unidad Central de Proceso (CPU), al reducir al mnimo los
tiempos de inactividad. Este es un factor muy importante a tener en cuenta en el
manejo de entornos interactivos, como es el caso del trabajo en la red, en donde

Programacin de Hilos: Introduccin 12 - 3


las velocidades de transmisin son mucho ms lentas que los requeridos por la
CPU en el procesamiento de esos datos, as como tambien durante el manejo
del sistema de archivos, lectura y grabacin, que son ms lentos que las
velocidades de la CPU en su proceso. Las animaciones y los juegos por
ejemplo, requieren de una mayor optimizacin de los recursos de CPU.
Al mostrarse un archivo o una imgen dentro de un navegador y acceder en
seguida a una direccin electronica o URL para una bsqueda, es un ejemplo de
aplicacin multihilo. Sin esta caracterstica, sera necesario esperar a que el
archivo se bajara de la red o se cargara la imagen para poder acceder a una
direccion URL para un proceso de bsqueda. Con la programacin multihilo,
se aprovecha el tiempo del procesador realizando otra tarea mientras se carga
una imgen o un archivo. Por obvias razones los vizualizadores o navegadores
en la red son multihilo.
La programacin multihilo permite acceder a los recursos de tiempo libre de la
CPU, mientras se realizan otras tareas. Es otra de las razones de importancia
para el estudio de sistemas basados en multihilos.
El modelo multihilo est inmerso en el interprete de Java, ya que las bibliotecas
de clases estan diseadas teniendo en mente este modelo. Los hilos forman
parte de todos los programas en Java. Un hilo dentro de un programa Java es un
procedimiento que se puede ejecutar independiente del resto de una aplicacin.
La siguiente figura ilustra esquemticamente un programa de un hilo y un
programa de dos hilos.

un hilo

Programa con un solo hilo


Figura 1

dos hilos

Programa con dos hilos

Programacin de Hilos: Introduccin 12 - 4


Cuando se ejecuta una aplicacin en Java, se ejecuta el mtodo main() en un
hilo dedicado, as como tambin los applets se ejecutan en hilos dedicados, lo
cual hace explcito que el concepto de hilo es esencial del trabajo en Java.
Un hilo puede detenerse sin afectar las otras partes del programa. Por ejemplo
en un hilo de animacin, se hace que los bucles de la animacin se detengan
durante un tiempo, un segundo, sin hacer que se pare el resto del programa.
Cuando un hilo se detiene o se bloquea, slo el se detiene y los dems
continan con su ejecucin.
La creacin de un hilo, como la necesidad de un procedimiento que se har de
manera concurrente con otros procedimientos, pasa por la definicin del
procedimiento que se llamar repetidamente, por ejemplo el procedimiento en
un applet que muestra una animacin, mientras se reproduce un clip de audio y
se capturan los clicks del ratn. Aqu la animacin consiste en exhibir a
intervalos de tempo una secuencia de imagenes de tal manera que se de la
sensacin de movimiento.

Ciclo de vida de un hilo


Un hilo tiene un ciclo de vida que va desde su creacin hsta su terminacin.
Durante su ciclo de vida cada uno de los hilos o tareas de una aplicacin puede
estar en diferentes estados, algunos de los cuales se indican a continuacin:

Nacido: Cuando se acaba de crear un hilo, se dice que est nacido, y


contina en ese estado hasta que se invoca el mtodo start() del hilo. La
siguiente sentencia crea un nuevo thread pero no lo arranca, por lo tanto
deja el thread en el estado de nacido.
Thread miHilo = new MiClaseThread();
Cuando un thread est en este estado, es slo un objeto Thread vaco o
nulo. No se han asignado recursos del sistema todava para el thread. As,
cuando un thread est en este estado, lo nico que se puede hacer es
arrancarlo con start().

Listo: Cuando se invoca el mtodo start() del hilo, se dice que est en
estado listo. El mtodo se arranca con la siguiente instruccin, para el caso
del hilo miHilo:
miHilo.start();

Ejecutable: cuando el mtodo start() se ejecuta, crea los recursos del


sistema necesarios para ejecutar el thread, programa el thread para

Programacin de Hilos: Ciclo de vida de un hilo 12 - 5


ejecutarse, y llama al mtodo run() del thread que se ejecuta en forma
secuencial. En este punto el thread est en el estado ejecutable. Se
denomina as puesto que todava no ha empezado a ejecutarse.

En ejecucin: Un hilo en estado de listo de la ms alta prioridad, pasa al


estado de ejecucin, cuando se le asignan los recursos de un procesador, o
sea cuando inicia su ejecucin. Aqu el thread est en ejecucin.Cada hilo
tiene su prioridad, hilos con alta prioridad se ejecutan preferencialmente
sobre los hilos de baja prioridad.

No ejecutable :Un hilo contina la ejecucin de su mtodo run(), hasta


que pasa al estado de no ejecutable originado cuando ocurre alguno de los
siguientes cuatro eventos:
Se invoca a su mtodo sleep().
Se invoca a su su mtodo suspend().
El thread utiliza su mtodo wait() para esperar una condicin variable.
El thread est bloqueado durante una solicitud de entrada/salida.
Por ejemplo, en el siguiente fragmento de codigo se pone a dormir
miHilo durante 10 segundos (10.000 milisegundos):
Thread miHilo = new MiClaseThread();
miHilo.start();
try {
miHilo.sleep(10000);
} catch (InterruptedException e){
}

Durante los 10 segundos que miHilo est dormido, incluso si el proceso


se vuelve disponible, miHilo no se ejecuta. Despus de 10 segundos,
miHilo se convierte en "Ejecutable" de nuevo y, si el procesador est
disponible se ejecuta. Para cada entrada en el estado "No Ejecutable",
existe una ruta de escape distinta y especfica que devuelve el thread al
estado "Ejecutable". Por ejemplo, si un thread ha sido puesto a dormir
dutante un cierto nmero de milisegundos deben pasar esos milisegundos
antes de volverse "Ejecutable" de nuevo.
La siguiente es una secuencia de las acciones a realizar para cada entrada
en el estado "No Ejecutable":

Programacin de Hilos: Ciclo de vida de un hilo 12 - 6


Si se ha puesto a dormir un hilo, deben pasar el nmero de milisegundos
especificados en sleep().
Si se ha suspendido un hilo, se debe llamar a su mtodo resume().
Si un hilo est esperando una condicin variable, siempre que el objeto
propietario de la variable renuncie mediante notify() o notifyAll
().
Si un hilo est bloqueado durante la I/O, cuando se complete la I/O.

Muerto: Un hilo pasa al estado de muerto cuando se termina su mtodo


run(), o cuando se ha invocado su mtodo stop(). En algn momento el
sistema dispondr entonces del hilo muerto. Un hilo puede morir de dos
formas:
Muerte natural: se produce cuando su mtodo run() sale normalmente.
Por ejemplo, el bucle while en este mtodo es un bucle que itera 100
veces y luego sale. Por tanto el hilo morir naturalmente cuando se llegue
al final de la iteracin, es decir se termina su mtodo run().
public void run() {
int i = 0;
while (i < 100) {
i++;
System.out.println("i = " + i);
}
}

Por muerte provocada: en cualquier momento llamando a su mtodo


stop(). El siguiente cdigo crea y arranca miHilo luego lo pone a
dormir durante 10 segundos. Cuando el thread actual se despierta, se lo
mata con miHilo.stop(). El mtodo stop() lanza un objeto
ThreadDeath hacia al hilo a eliminar. El thread moriri cuando reciba
realmente la excepcin ThreadDeath.
Thread miHilo = new MiClaseThread();
miHilo.start();
try {
Thread.currentThread().sleep(10000);
} catch (InterruptedException e){
}miHilo.stop();

El mtodo stop() provoca una terminacin sbita del mtodo run() del hilo.
Si el mtodo run() estuviera realizando clculos sensibles, stop() podra
dejar el programa en un estado inconsistente. Normalmente, no se debera

Programacin de Hilos: Ciclo de vida de un hilo 12 - 7


llamar al mtodo stop() pero si se debera proporcionar una terminacin
educada como la seleccin de una bandera que indique que el mtodo run()
debera salir. El mtodo stop() se encuentra depreciado en la version
JDK1.2.1.

Bloqueado: un hilo se encuentra en el estado bloqueado cuando el hilo


realiza una solicitud de entrada/salida. Cuando termina la entrada/salida que
estaba esperando, un hilo bloqueado queda en el estado listo.

La clase Thread
Como ya se ha establecido antes, un thread es un hilo de ejecucin de un
programa. La Maquina Virtual de Java (JVM) permite la ejecucin de
concurrente de mltiples hilos. En la clase Thread se encapsula todo el control
necesario sobre los hilos de ejecucin o tareas. Un objeto Thread se lo puede
entender como el panel de control sobre una tarea o hilo de ejecucin. Dispone
de mtodos para controlar el comportamiento de las tareas, los cuales se tratarn
ms adelante.
Los siguientes son los principales mtodos de Thread:

Metodos de la clase Thread


static int

activeCount()
Retorna el mmero de el hilo activo en el grupo de
hilos.

void

checkAccess()
Chequea si eel hilo que se esta ejecutando tiene
permiso para modificar este hilo.

static Thr
ead

currentThread()
Retorna el objeto Thread que representa a la tarea
que se est ejecutando.

void

destroy()
Destruye la tarea actual.

String

getName()
Retorna el nombre actual del hilo, asignado con
setName().

int

getPriority()
Retorna un valor entre 1 y 10, correspondiente a la
prioridad de la tarea en ejecucin.

Programacin de Hilos: La clase Thread 12 - 8

Metodos de la clase Thread


ThreadGro
up

void

static boo
lean

getThreadGroup()
Retorna el grupo de hilo a que pertenece el hilo
actual.
interrupt()
Interrumpe el hilo.
interrupted()
Chequea si el hilo actual se ha interrumpido.

boolean

isAlive()
Chequea si el hilo esta vivo.

boolean

isDaemon()
Chequea si el hilo es un hilo de servicio a otros.

boolean

isInterrupted()
Chequea si el hilo ha sido interrumpido.

void

join()
Espera a que el hilo se muera.

void

join(long millis)
Espera hasta millis milisegundos para que el hilo
muera.

void

run()
Constituye el cuerpo de una tarea o hilo. Es
llamado por el mtodo start(), despues que la tarea
apropiada del sistema se haya inicializado. La tarea actual
se detendr cada vez que run() devuelva el control. Este es
el nico mtodo de la interfaz Runnable.

void

setDaemon(boolean on)
Marca este hilo como un hilo de servi iusuario o
hilo demonio.

void

setName(String name)
Permite asignarle a la tarea un nombre con el cual
se la puede identificar..

void

setPriority(int newPriority)
Cambia la prioridad de la tarea, entre los valores 1 y
10.

Programacin de Hilos: La clase Thread 12 - 9

Metodos de la clase Thread


static voi
d

sleep(long millis)
Provoca que el intrprete de Java ponga la tarea a
dormir durante un tiempo indicado en milisegundos,
transcurrido dicho tiempo la tarea estar disponible para
ejecucin.

void

start()
Le indica al interprete de Java que cree un contexto
de ejecucin de una tarea e inicie su ejecucin. Acto
seguido se invoca el mtodo run() de esta tarea en el
nuevo contexto de ejecucin. Debe tenerse cuidado de
llamar a start() no ms de una vez para una tarea
determinada.

String

toString()
Retorna la cadena que representa el hilo, que
incluye nombre, prioridad y grupo del hilo.

static voi
d

yield()
Hace que el interprete detenga temporalmente la
ejecucin del objeto del hilo y permita que el siguiente
hilo disponible se ejecute.

Prioridades
El intrprete de Java utiliza prioridades, como valores enteros, para determinar
el trato que le debe dar a cada hilo respecto de los dems. La prioridad de un
hilo se utiliza para determinar cando se pasa a ejecutar otro hilo, lo cual se
denomina cambio de contexto de ejecucin. Hilos con alta prioridad se ejecutan
preferencialmente sobre hilos de menor prioridad.
La clase Thread tiene tres constantes predefinidas para asignar la prioridad:
MIN_PRIORITY (prioridad 1), NORM_PRIORITY (prioridad 5) y
MAX_PRIORITY (prioridad 10). Los procesos a nivel de usuario se
recomienda que tengan una prioridad alrededor de 5, tareas en segundo plano
as como de redibujo de la pantalla o entrada/salida por la red, con una prioridad
cercana a la minima.
Las reglas que determinan cuando se hace una conmutacin o cambio de
contexto son:

Un hilo le cede voluntariamente el control a otro, por ejemplo, cuando

Programacin de Hilos: Prioridades 12 - 10


abandona explcitamente, al quedarse dormido, o bloqueado en espera de una
operacin de entrada/salida pendiente. Se seleccionara para la CPU aquel
hilo que estando preparado para ejecucin, tenga la prioridad ms alta.

Un hilo es desalojado por otro de prioridad ms alta, independientemente de


lo que el hilo desalojado estuviera haciendo.

Si dos o ms hilos de igual prioridad compiten por el recurso, se pueden


repartir el tiempo de la CPU mediante algoritmos especiales (algoritmo
circular o round-robin). Sin embargo se pueden presentar problemas, ante la
igualdad de prioridad, debido a que los sistemas operacionales tratan de
manera diferente la conmutacin de contexto.

El hilo principal
Cuando la Maquina Virtual de Java arranca la ejecucin de un programa, ya hay
un hilo ejecutndose, denominado hilo principal del programa, que se ejecuta
cuando comienza el programa. Su importancia radica en que:

Es el hilo desde el cual se crearn el resto de hilos del programa.

Debe ser el ltimo hilo que termine su ejecucin, ya que cuando este hilo
finaliza, el programa termina.

El hilo principal se puede controlar a travs de un objeto Thread, mediante el


llamado al mtodo currentThread(), el cual devuelve una referencia al hilo
donde es llamado. Ya con la referencia al hilo principal se lo puede controlar,
como a cualquier otro hilo.

Creacin de hilos
El mtodo run() le permite a un hilo realizar su tarea, ya que su cdigo
implementa el comportamiento de ejecucin de la clase Thread, pudiendo
realizar cualquier operacin que sea codificable en instrucciones Java.
La clase Thread implementa un hilo genrico que por defecto no realiza nada,
es decir que, la implementacin de ese mtodo dentro de la clase es vaca. La
clase Thread (que es de por s un objeto Runnable) permite que un objeto
Runnable provea un mtodo run() ms interesante para los hilos. Hay dos
manera de crear hilos:

Extensin de la clase Thread


Mediante la obtencin de subclases de la clase Thread y creando luego una

Programacin de Hilos: Creacin de hilos

12 - 11

instancia de esta clase. Esta nueva clase debe sobreescribir el mtodo run de la
clase Thread que es el punto de entrada del nuevo hilo. Dentro de la clase que
utilice la instancia de la clase Thread, se debe llamar al mtodo start() para que
inicie la ejecucin del nuevo hilo.
La siguiente es la forma general del cuerpo de una clase hilo mediante extensin
de la clase Thread:
public class NombreClase extends Thread{
// clase que extiende a Thread
.....
public void run(){
// cuerpo para sobreescribir run()
.....
}
}

Dentro de la clase que usa a NombreClase, la siguiente es la forma del


cdigo para crear el hilo e iniciar su ejecucin mediante el llamado al mtodo
start():
NombreClase unHilo = new NombreClase();
.
.
unHilo.start();

Ejemplo 1: Un hilo que genere los primeros N numeros enteros, intervalos de


100 miullisegundos, por el mtodo de extensin de Thread tendra la siguiente
estructura:
class GeneraEnteros extends Thread {
int n;
GeneraEnteros(int valorN) {
n = valorN;
}
public void run() {
// calcula los n primeros terminos de la serie
. . .
}
}

Con el siguiente cdigo se crea el hilo y se inicia su ejecucin mediante el


llamado al mtodo start() en la clase que usa a GeneraEnteros:
GeneraEnteros serie = new GeneraEnteros(10);
serie.start();

Veamos el ejemplo codificado completamente, para la generacin de los

Programacin de Hilos: Creacin de hilos

12 - 12

primeros numeros enteros:


public class GeneraEnteros extends Thread {
int n;
public GeneraEnteros(int valorN) {
n = valorN; }
public void run() {
for (int i = 0; i < n; i++) {
System.out.println("Numero "+i);
try {
sleep(1000);
} catch (InterruptedException e) {
System.out.println("Interrupcion hilo ");}
}
System.out.println("Termina hilo " );
}
}
import javax.swing.JOptionPane;
public class UsaGeneraEnteros{
public static void main (String[] args) {
int numero = Integer.parseInt
(JOptionPane.showInputDialog("Cuantos terminos?"));
GeneraEnteros serie =new GeneraEnteros(numero);
serie.start();
}
}

Ejecutese el programa principal y observe que para un valor de 10, se obtiene la


siguiente salida en la ventana del sistema:
Numero 0
Numero 1
Numero 2
Numero 3
Numero 4
Numero 5
Numero 6
Numero 7
Numero 8
Numero 9
Termina hilo
Press any key to continue...

Observese que en la clase principal no se ha colocado la instruccin del mtodo


System.exit(0), ya que esto terminaria la ejecucin del hilo del metodo
main(), antes de terminar el hilo serie.
Ejemplo 2: Se a continuacin una aplicacin que maneja dos hilos. El hilo

Programacin de Hilos: Creacin de hilos

12 - 13

principal corresponde a main() y el segundo hilo a Hilo 1. Se utiliza la clase


GeneraEnteros, con un constructor que le ingresa el nombre del hilo y la
duracin del tiempo de detencion del hilo:
class GeneraEnteros extends Thread {
int n, tiempo;
String nombre;
public GeneraEnteros(int valorN, String nombreHilo, int
retardo) {
n = valorN;
nombre = nombreHilo;
tiempo=retardo; }
public void run() {
for (int i = 0; i < n; i++) {
System.out.println("Numero "+i+ " en " + nombre);
try {
sleep(tiempo);
} catch (InterruptedException e) {
System.out.println("Interrupcion "+ nombre);}
}
System.out.println("Termina "+nombre );
}
}
import javax.swing.JOptionPane;
public class UsaGeneraEnteros{
public static void main (String[] args) {
int numero = Integer.parseInt
(JOptionPane.showInputDialog("Cuantos terminos?"));
GeneraEnteros serie =new GeneraEnteros(numero, "hilo 1",
1000);
serie.start();
try {
for (int i = 0; i <5; i++) {
System.out.println("valor i = "+i+" en Hilo principal
en main()");
Thread.sleep(3000);
}
}catch (InterruptedException e){
System.out.println("Interrupcin del hilo principal en
main()");
}
System.out.println("Termina Hilo principal en main()");
}
}

Cuyos resultados para un valor de 10 elementos de la serie es:


valor i = 0 en Hilo principal en main()
Numero 0 en hilo 1
Numero 1 en hilo 1

Programacin de Hilos: Creacin de hilos


Numero 2 en hilo 1
valor i = 1 en Hilo principal en
Numero 3 en hilo 1
Numero 4 en hilo 1
Numero 5 en hilo 1
valor i = 2 en Hilo principal en
Numero 6 en hilo 1
Numero 7 en hilo 1
Numero 8 en hilo 1
valor i = 3 en Hilo principal en
Numero 9 en hilo 1
Termina hilo 1
valor i = 4 en Hilo principal en
Termina Hilo principal en main()
Exit code: 0
No Errors

12 - 14

main()

main()

main()
main()

Ejemplo 3: Acontinuacin la aplicacin UsaGeneraEnteros que maneja tres


hilos: el hilo principal que corresponde a main() y los hilos, Hilo 1 e Hilo 2,
instancias de la clase GeneraEnteros, cuya version es la misma que en el
ejemplo anterior:
import javax.swing.JOptionPane;
public class UsaGeneraEnteros{
public static void main (String[] args) {
int numero = Integer.parseInt
(JOptionPane.showInputDialog("Cuantos terminos?"));
GeneraEnteros serie1 =new GeneraEnteros(numero, "hilo
1", 1000);
serie1.start();
GeneraEnteros serie2 =new GeneraEnteros(numero, "hilo
2", 100);
serie2.start();
try {
for (int i = 0; i <5; i++) {
System.out.println("valor i = "+i+" en Hilo principal
en main()");
Thread.sleep(3000);
}
}catch (InterruptedException e){
System.out.println("Interrupcin del hilo principal en
main()");
}
System.out.println("Termina Hilo principal en main()");
}
}

Cuyos resultados, al ejecutar UsaGeneraEnteros, para 5 elementos de la serie


son:

Programacin de Hilos: Creacin de hilos


valor i = 0 en Hilo principal en
Numero 0 en hilo 1
Numero 0 en hilo 2
Numero 1 en hilo 2
Numero 2 en hilo 2
Numero 3 en hilo 2
Numero 4 en hilo 2
Termina hilo 2
Numero 1 en hilo 1
Numero 2 en hilo 1
valor i = 1 en Hilo principal en
Numero 3 en hilo 1
Numero 4 en hilo 1
Termina hilo 1
valor i = 2 en Hilo principal en
valor i = 3 en Hilo principal en
valor i = 4 en Hilo principal en
Termina Hilo principal en main()
Exit code: 0
No Errors

12 - 15

main()

main()

main()
main()
main()

Ejemplo 3: Acontinuacin la aplicacin UsaGeneraEnteros que maneja cuatro


hilos: el hilo principal que corresponde a main() y los hilos, Hilo 1, Hilo 2, e
Hilo 3, instancias de la clase GeneraEnteros, cuya version es la misma que en el
ejemplo anterior:
import javax.swing.JOptionPane;
public class UsaGeneraEnteros{
public static void main (String[] args) {
int numero = Integer.parseInt
(JOptionPane.showInputDialog("Cuantos terminos?"));
GeneraEnteros serie1 =
new GeneraEnteros(numero, "hilo 1", 1000);
serie1.start();
GeneraEnteros serie2 =
new GeneraEnteros(numero, "hilo 2", 100);
serie2.start();
GeneraEnteros serie3 =
new GeneraEnteros(numero, "hilo 3", 400);
serie3.start();
try {
for (int i = 0; i <5; i++) {
System.out.println("valor i = "+i+" en Hilo principal
en main()");
Thread.sleep(3000);
}
}catch (InterruptedException e){
System.out.println("Interrupcin del hilo principal en
main()");
}
System.out.println("Termina Hilo principal en main()");

Programacin de Hilos: Creacin de hilos

12 - 16

}
}

Cuyos resultados, al ejecutar UsaGeneraEnteros, para 5 elementos de la serie


son:
valor i = 0 en Hilo principal en
Numero 0 en hilo 1
Numero 0 en hilo 2
Numero 0 en hilo 3
Numero 1 en hilo 2
Numero 2 en hilo 2
Numero 3 en hilo 2
Numero 1 en hilo 3
Numero 4 en hilo 2
Termina hilo 2
Numero 2 en hilo 3
Numero 1 en hilo 1
Numero 3 en hilo 3
Numero 4 en hilo 3
Numero 2 en hilo 1
Termina hilo 3
valor i = 1 en Hilo principal en
Numero 3 en hilo 1
Numero 4 en hilo 1
Termina hilo 1
valor i = 2 en Hilo principal en
valor i = 3 en Hilo principal en
valor i = 4 en Hilo principal en
Termina Hilo principal en main()
Exit code: 0
No Errors

main()

main()

main()
main()
main()

Implementacin de la interfaz Runnable


Por este mtodo se debe implementar la interfaz Runnable para la cual se debe
sobreescribir el mtodo run(). Una instancia de la clase puede ser creada y
pasarla como argumento cuando se cree Thread e iniciarla.
La interfaz Runnable debe ser implementada por cualquier clase cuyas
instancias sean ejecutadas por un hilo. Dicha clase debe implementar el mtodo
run().
La implementacin de la interface Runnable es la forma ms habitual de crear
tareas, ya que proporciona al desarrollador una forma para agrupar el trabajo de
infraestructura de la clase. La interfaz establece el trabajo a realizar y la clase o
clases que la implementan, indican como realizar ese trabajo. Se puede
construir un hilo sobre cualquier objeto que implemente la interfaz Runnable.
Para implementar esta interfaz, una clase slo tiene que implementar el mtodo
run(). Dentro de run() se define el cdigo que constituye el nuevo hilo. Despues

Programacin de Hilos: Creacin de hilos

12 - 17

de haberse creado una clase que implemente la interfaz Runnable se requiere


crear un objeto del tipo Thread dentro de esa clase, usando cualquiera de los
constructores de Thread.
La siguiente es la forma general del cuerpo de un hilo mediante implementacin
de la interfaz Runnable dentro de un applet, ya que, obviamente un applet no
puede extender ademas a Thread, puesto que ya extiende a Applet:
public class MiClase extends Applet implements Runnable{
Thread unHilo;
public void start(){
if (unHilo == null){
unHilo = new Thread(this, nombre del hilo);
unHilo.start();
....
}
}
public void run(){
if(unHilo!= null){
// cuerpo del hilo
....
}
}
}

Para una clase diferente a applet se tiene la siguiente forma general:


publi class MiClase implements Runnable {
Thread unHilo;
MiClase(){
unHilo=new Thread();
}
public void run() {
if(unHilo!= null){
// cuerpo del hilo
....
}
}
}

La siguiente forma general crea el hilo e inicia su ejecucin en la clase que usa a
MiClase:
MiClase miHilo = new MiClase();
new Thread(miHilo).start();

Ejemplo 4: Para el caso de la generacin de los numeros enteros, se utiliza el


cuerpo de la clase GeneraEnteros, modifinadolo como GeneraEnterosRun,
mediante la implementacin de la interfaz Runnable, lo mismo que la
modificacin en la clase UsaGeneraEnterosRun, cuyas versiones para el manejo

Programacin de Hilos: Creacin de hilos

12 - 18

del hilo principal del mtodo main() y un hilo de la clase GeneraEnterosRun se


ilustra a continuacin:
class GeneraEnterosRun implements Runnable{
int n, tiempo;
String nombre;
Thread unHilo;
public GeneraEnterosRun(int valorN, String nombreHilo, int
retardo) {
n = valorN;
nombre = nombreHilo;
tiempo=retardo;
unHilo = new Thread();}
public void run() {
for (int i = 0; i < n; i++) {
System.out.println("Numero "+i+ " en " + nombre);
try {
unHilo.sleep(tiempo);
} catch (InterruptedException e) {
System.out.println("Interrupcion "+ nombre);}
}
System.out.println("Termina "+nombre );
}
}
import javax.swing.JOptionPane;
public class UsaGeneraEnterosRun{
public static void main (String[] args) {
int numero = Integer.parseInt
(JOptionPane.showInputDialog("Cuantos terminos?"));
GeneraEnterosRun serie1 =new GeneraEnterosRun(numero,
"hilo 1", 1000);
new Thread(serie1).start();
try {
for (int i = 0; i <5; i++) {
System.out.println("valor i = "+i+" en Hilo principal
en main()");
Thread.sleep(3000);
}
}catch (InterruptedException e){
System.out.println("Interrupcin del hilo principal en
main()");
}
System.out.println("Termina Hilo principal en main()");
}
}

Los siguientes son los resultados obtenidos con un valor de 10 en el numero de


terminos a generar:

Programacin de Hilos: Creacin de hilos


valor i = 0 en Hilo principal en
Numero 0 en hilo 1
Numero 1 en hilo 1
Numero 2 en hilo 1
valor i = 1 en Hilo principal en
Numero 3 en hilo 1
Numero 4 en hilo 1
Numero 5 en hilo 1
valor i = 2 en Hilo principal en
Numero 6 en hilo 1
Numero 7 en hilo 1
Numero 8 en hilo 1
valor i = 3 en Hilo principal en
Numero 9 en hilo 1
Termina hilo 1
valor i = 4 en Hilo principal en
Termina Hilo principal en main()
Exit code: 0
No Errors

12 - 19

main()

main()

main()

main()
main()

Ejemplo 5: Veamos a continuacin el mismo problema pero resuelto mediante


el applet AppleGeneraEnteros:
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Color;
import javax.swing.*;
public class AppletGeneraEnteros extends Applet implements
Runnable {
Thread unHilo;
int i, numero,tiempo, x, y;
String nombre;
public void start(){
nombre =JOptionPane.showInputDialog("Nombre hilo?" );
numero=Integer.parseInt(JOptionPane.showInputDialog("Numero de
terminos ?" ));
tiempo= Integer.parseInt(JOptionPane.showInputDialog
("Retardo ?" ));
if(unHilo== null){
unHilo = new Thread(this);
unHilo.start();
x=10; y=10;
}
}
public void update(Graphics g){
setBackground(Color.white);
g.drawString("numero "+i+" de = "+nombre, x,y);
}
public void run() {
Thread thisThread = Thread.currentThread();
while (unHilo == thisThread){
for(i=0; i < numero; i++){
y=y+20;
repaint();

Programacin de Hilos: Creacin de hilos

12 - 20

try {
Thread.sleep(tiempo);
} catch (InterruptedException e) {
System.out.println("Interrupcion del hilo ");}
}
System.out.println("Termina hilo " );
unHilo= null;
}
}
public void paint(Graphics g){
g.drawString("Generacin enteros ",x,y);
}
}

Y su respectivo archivo HTML:


<HTML>
<APPLET CODE="AppletGeneraEnteros.class" WIDTH="400"
HEIGHT="400">
</APPLET>
</HTML>

Cuyo resultado se obtiene, para 10 terminos, segn la siguiente pantalla del


applet:

Programacin de Hilos: Creacin de hilos

12 - 21

Ejemplo 6: A continuacin una aplicacin que maneja tres hilos. El hilo


principal corresponde a main() y los hilos, Hilo 1 e Hilo 2 creados como
instancias de GeneraEnterosRun, que implementa Runnable:
import javax.swing.JOptionPane;
public class UsaGeneraEnterosRun{
public static void main (String[] args) {
int numero = Integer.parseInt
(JOptionPane.showInputDialog("Cuantos terminos?"));
GeneraEnterosRun serie1 =new GeneraEnterosRun(numero,
"hilo 1", 1000);
new Thread(serie1).start();
GeneraEnterosRun serie2 =new GeneraEnterosRun(numero,
"hilo 2", 200);
new Thread(serie2).start();
try {
for (int i = 0; i <5; i++) {
System.out.println("valor i = "+i+" en Hilo principal
en main()");
Thread.sleep(3000);
}
}catch (InterruptedException e){
System.out.println("Interrupcin del hilo principal en
main()");
}
System.out.println("Termina Hilo principal en main()");
}
}

Los resultados al ejecutarse son los siguientes:


valor i = 0 en Hilo principal en main()
Numero 0 en hilo 1
Numero 0 en hilo 2
Numero 1 en hilo 2
Numero 2 en hilo 2
Numero 3 en hilo 2
Numero 4 en hilo 2
Numero 5 en hilo 2
Numero 1 en hilo 1
Numero 6 en hilo 2
Numero 7 en hilo 2
Numero 8 en hilo 2
Numero 9 en hilo 2
Numero 2 en hilo 1
Termina hilo 2
valor i = 1 en Hilo principal en main()
Numero 3 en hilo 1
Numero 4 en hilo 1
Numero 5 en hilo 1
valor i = 2 en Hilo principal en main()
Numero 6 en hilo 1

Programacin de Hilos: Creacin de hilos

12 - 22

Numero 7 en hilo 1
Numero 8 en hilo 1
valor i = 3 en Hilo principal en main()
Numero 9 en hilo 1
Termina hilo 1
valor i = 4 en Hilo principal en main()
Termina Hilo principal en main()
Exit code: 0
No Errors

Ejemplo 7: A continuacin se presenta un programa que permite dibujar la


fecha y la hora en la ventana de un applet que implementa Runnable, por medio
de la creacin de un hilo:
import
import
import
import

java.awt.Graphics;
java.awt.Font;
java.util.Date;
java.applet.Applet;

public class Reloj01 extends Applet


implements Runnable {
Font theFont = new Font("TimesRoman",Font.BOLD,15);
Date theDate;
Thread runner;
public void start() {
if (runner == null) {
runner = new Thread(this);
runner.start();
}
}
public void stop() {
if (runner != null) {
runner = null;
}
}
public void run() {
Thread thisThread = Thread.currentThread();
while (runner == thisThread) {
repaint();
try {
Thread.sleep(1000);
} catch (InterruptedException e) { }
}
}
public void paint(Graphics screen) {
theDate = new Date();
screen.setFont(theFont);

Programacin de Hilos: Creacin de hilos

12 - 23

screen.drawRect(1,1, getSize().width-2, getSize().


height-2);
screen.drawString("" + theDate.toString(), 20, 20);
}
}
<applet code="Reloj01.class" width=250 height=40>
</applet>

La salida es la siguiente:

Ejemplo 8: El siguiente ejemplo dibuja 100 circulos a intervalos de 500


milisegundos en la ventana del applet.
import java.awt.Graphics;
import java.applet.Applet;
import java.awt.Font;
import java.util.Date;
import java.awt.Color;
public class Circulos01 extends Applet
implements Runnable {
int x1, y1;
Thread runner;
public void init(){
x1 = (int)(Math.random()*(getSize().width-10));
y1 = (int)(Math.random()*(getSize().height-10));
}
public void start() {
if (runner == null) {
runner = new Thread(this);
runner.start();
}
}
public void update(Graphics g){
setBackground(Color.white);
g.drawOval(x1,y1,20,20);
x1 = (int)(Math.random()*(getSize().width-20));
y1 = (int)(Math.random()*(getSize().height-20));
}
public void stop() {
if (runner != null) {
runner = null;

Programacin de Hilos: Creacin de hilos

12 - 24

}
}
public void run() {
Thread thisThread = Thread.currentThread();
while (runner == thisThread ) {
for(int i=1; i <= 100;i++){
repaint();
try {
Thread.sleep(500);
} catch (InterruptedException e) { }
}
System.out.println("Termina hilo");
runner= null;
}
}
public void paint(Graphics g) {
}
}

Ejemplo 9: Aqu se ha adaptado un programa que permite generar circulos y


ponerlos en movimiento. La generacin de circulos se hace mediante un boton,
el cual al ser pulsado lo genera aleatoriamente en cualquier parte de la ventana.
Se deja para estudio del lector y para que ejercite modificaciones que pueda
realizar.

Programacin de Hilos: Creacin de hilos

12 - 25

El programa fu tomado de Core Web Programing de PrenticeHall, 1997,


desarrollado por Mary Hall, quien autoriza su libre uso o adaptacin.
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
/* Movimiento de circulos alrededor de la pantalla. */
public class Bounce extends Applet
implements Runnable,
ActionListener {
private Vector circles;
private int width, height;
private Button startButton, stopButton;
private Thread animationThread = null;
public void init() {
setBackground(Color.white);
width = getSize().width;
height = getSize().height;
circles = new Vector();
startButton = new Button("Inicia Circulo");
startButton.addActionListener(this);
add(startButton);
stopButton = new Button("Detiene Circulos");
stopButton.addActionListener(this);
add(stopButton);
}
/* Cuando el boton "start" button is presionado se inicia un
hilo de animacion si no se ha iniciado. De cualquier manera
agrega un circulo al Vector de circulos que estan en movimiento.
Cuando el boton Stop se presiona suspende el hilo y limpia el
Vector. */
public void actionPerformed(ActionEvent event) {
// para manejar los eventos de los botones
if (event.getSource() == startButton) {
if (circles.size() == 0) {
// Erase any circles from previous run.
getGraphics().clearRect(0, 0, getSize().width,
getSize().height);
animationThread = new Thread(this);
animationThread.start();
}
int radius = 25;
int x = radius + randomInt(width - 2 * radius);
int y = radius + randomInt(height - 2 * radius);
int deltaX = 1 + randomInt(10);
int deltaY = 1 + randomInt(10);
circles.addElement(new MovingCircle(x, y, radius,
deltaX,
deltaY));
} else if (event.getSource() == stopButton) {
if (animationThread != null) {

Programacin de Hilos: Creacin de hilos

12 - 26

animationThread.stop();
circles.removeAllElements();
}
}
repaint();
}
/* En cada iteracion se llama a paint() y se hace una pausa. El
mtodo paint(9 mueve los circulos y los dibuja */
public void run() {
while(true) {
repaint();
pause(100);
}
}
public void update(Graphics g) {
paint(g);
}
/* Borra cada circulo en su posicion anterior, lo mueve y lo
dibuja de nuevo en la nueva posicion */
public void paint(Graphics g) {
MovingCircle circle;
for(int i=0; i<circles.size(); i++) {
circle = (MovingCircle)circles.elementAt(i);
g.setColor(getBackground());
circle.draw(g); // vieja posicion
circle.move(width, height);
g.setColor(getForeground());
circle.draw(g); // nueva posicion
}
}
private int randomInt(int max) {
double x =
Math.floor((double)(max + 1) * Math.random());
return((int)(Math.round(x)));
}
// Duerme por un tiempo
private void pause(int milliseconds) {
try {
Thread.sleep((long)milliseconds);
} catch(InterruptedException ie) {}
}
}

import java.awt.*;
/* Clase para almacenar x, y, radius, con un mtodo de dibujo */
public class SimpleCircle {

Programacin de Hilos: Creacin de hilos

12 - 27

private int x, y, radius;


public SimpleCircle(int x, int y, int radius) {
setX(x);
setY(y);
setRadius(radius);
}
/* Dado un objeto Graphics, dibuja un SimpleCircle centrado
alrededor de su posicion actual */
public void draw(Graphics g) {
g.fillOval(x - radius, y - radius,
radius * 2, radius * 2);
}
public int getX() { return(x); }
public void setX(int x) { this.x = x; }
public int getY() { return(y); }
public void setY(int y) { this.y = y; }
public int getRadius() { return(radius); }
public void setRadius(int radius) {
this.radius = radius;
}
}
/* Extension de SimpleCircle que puede moverse alrededor de
valores deltaX y deltaY. El movimiento continua en una direccion
hasta que el borde del circulo toque una pared de la ventana, en
cuyo caso rebota en otra direccion. */
public class MovingCircle extends SimpleCircle {
private int deltaX, deltaY;
public MovingCircle(int x, int y, int radius,
int deltaX, int deltaY) {
super(x, y, radius);
this.deltaX = deltaX;
this.deltaY = deltaY;
}
public void move(int windowWidth, int windowHeight) {
setX(getX() + getDeltaX());
setY(getY() + getDeltaY());
bounce(windowWidth, windowHeight);
}
private void bounce(int windowWidth,
int windowHeight) {
int x = getX(), y = getY(), radius = getRadius(),
deltaX = getDeltaX(), deltaY = getDeltaY();
if ((x - radius < 0) && (deltaX < 0))
setDeltaX(-deltaX);
else if ((x + radius > windowWidth) && (deltaX > 0))
setDeltaX(-deltaX);
if ((y -radius < 0) && (deltaY < 0))
setDeltaY(-deltaY);
else if((y + radius > windowHeight) && (deltaY > 0))

Programacin de Hilos: Creacin de hilos

12 - 28

setDeltaY(-deltaY);
}
public int getDeltaX() {
return(deltaX);
}
public void setDeltaX(int deltaX) {
this.deltaX = deltaX;
}
public int getDeltaY() {
return(deltaY);
}
public void setDeltaY(int deltaY) {
this.deltaY = deltaY;
}
}
<HTML>
<BODY BGCOLOR="WHITE"><H1>Circulos rebotando</H1>
<TABLE BORDER=5> <TR><TH>
<APPLET CODE="Bounce.class" WIDTH=600 HEIGHT=400> </APPLET>
</TABLE></BODY> </HTML>

El siguiente es un resultado de ejecutar el applet:

También podría gustarte