You are on page 1of 10

Hilos (thread)

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.
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 la
importancia para el estudio de sistemas basados en multihilos.

Ciclo de vida de un hilo


Un hilo tiene un ciclo de vida que va desde su creacin hasta 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:
Nace: Declarado pero todava no se ha ejecutado start()
Thread miHilo = new MiClaseThread();
Listo: Cuando se invoca el mtodo start()del hilo, se dice que est en estado listo.
miHilo.start();
En ejecucin: cuando el mtodo start() se ejecuta, crea los recursos del sistema necesarios para ejecutar el thread, programa el thread para
ejecutarse, y llama al mtodo run() del thread que se ejecuta en forma secuencial. 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 mtodo suspend().
El thread utiliza su mtodo wait() para esperar una condicin variable.
El thread est bloqueado durante una solicitud de entrada/salida.
Ejemplo:
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 especifica que devuelve el thread al estado "Ejecutable". Por ejemplo, si un thread ha sido puesto a dormir durante
un cierto nmero de milisegundos deben pasar esos milisegundos antes de volverse "Ejecutable" de nuevo.
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.
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.
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
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:

El hilo principal
Cuando la Mquina 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.

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:

1)

Extensin de la clase Thread

Mediante la obtencin de subclases de la clase Thread y creando luego una 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 millisegundos, por el mtodo de extensin de Thread
tendra la siguiente estructura:
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 " );
}
}
// CLASE QUE USA EL HILO CREADO (GeneraEnteros)
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 9
Termina hilo
Press any key to continue...
Observe que en la clase principal no se ha colocado la instruccin del mtodo System.exit(0), ya que esto terminara la ejecucin del hilo del
mtodo main(), antes de terminar el hilo serie.

Ejemplo 2: Se tiene una aplicacin que maneja dos hilos. El hilo 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 detencin 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 );
}
}
// CLASE QUE USA EL HILO CREADO (GeneraEnteros)
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 7 en hilo 1
Numero 0 en hilo 1
Numero 8 en hilo 1
Numero 1 en hilo 1
valor i = 3 en Hilo principal en main()
Numero 2 en hilo 1
Numero 9 en hilo 1
valor i = 1 en Hilo principal en main()
Termina hilo 1
Numero 3 en hilo 1
valor i = 4 en Hilo principal en main()
Numero 4 en hilo 1
Termina Hilo principal en main()
Numero 5 en hilo 1
Exit code: 0
valor i = 2 en Hilo principal en main()
No Errors
Numero 6 en hilo 1
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:
valor i = 0 en Hilo principal en main()
Numero 0 en hilo 1
Numero 3 en hilo 1
Numero 0 en hilo 2
Numero 4 en hilo 1
Numero 1 en hilo 2
Termina hilo 1
Numero 2 en hilo 2
valor i = 2 en Hilo principal en main()
Numero 3 en hilo 2
valor i = 3 en Hilo principal en main()
Numero 4 en hilo 2
valor i = 4 en Hilo principal en main()
Termina hilo 2
Termina Hilo principal en main()
Numero 1 en hilo 1
Exit code: 0
Numero 2 en hilo 1
No Errors
valor i = 1 en Hilo principal en
main()

2) 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 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. Despus 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:
public 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 nmeros enteros, se utiliza el cuerpo de la clase GeneraEnteros, modificndolo como
GeneraEnterosRun, mediante la implementacin de la interfaz Runnable, lo mismo que la modificacin en la clase UsaGeneraEnterosRun,
cuyas versiones para el manejo 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 nmero de trminos a generar:
valor i = 0 en Hilo principal en main()
Numero 7 en hilo 1
Numero 0 en hilo 1
Numero 8 en hilo 1
Numero 1 en hilo 1
valor i = 3 en Hilo principal en main()
Numero 2 en hilo 1
Numero 9 en hilo 1
valor i = 1 en Hilo principal en main()
Termina hilo 1
Numero 3 en hilo 1
valor i = 4 en Hilo principal en main()
Numero 4 en hilo 1
Termina Hilo principal en main()
Numero 5 en hilo 1
Exit code: 0
valor i = 2 en Hilo principal en main()
No Errors
Numero 6 en hilo 1

Ejemplo 5: 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
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 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;
}
}
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:

You might also like