📄 java14.htm
字号:
<HTML>
<HEAD>
<META NAME="GENERATOR" CONTENT="Internet Assistant for Microsoft Word 2.0z">
<TITLE>Untitled</TITLE>
</HEAD>
<BODY background=/iconos/1.gif TEXT=000000 LINK=FF0000 VLINK=A62A2A>
<H1>Java en hebras<BR>
</H1>
<P>
La clase anterior usamos, en el último ejemplo, un concepto
al que vamos a dedicar ahora nuestra atención: los <I>threads</I>.
<P>
La traducción literal de <I>thread</I> es hilo o hebra,
y se utiliza también para referirse al hilo de un discurso.
El concepto de threads en los ambientes y sistemas operativos
es un poco complejo de explicar pero sencillo de entender: independientemente
del sistema elegido, puede pensarse que un thread es algo así
como el lugar de ejecución de un programa.
<P>
En la mayoría de los programas que hemos visto, hemos usado
un solo thread; es decir que un programa comienza y su ejecución
sigue un camino único: como un monólogo.
<P>
Java es multithreading. Esto significa algo así como que
tiene capacidad de diálogo, y más aún: puede
ejecutar muchos threads en paralelo, como si tratáramos
de una conversación múltiple y simultánea.
<P>
No confundir aquí multithreading con la capacidad de ejecutar
varios programas a la vez. Esta es una posibilidad, pero también
un mismo programa puede utilizar varios threads ("caminos
de ejecución"?) simultáneamente.
<P>
Esto, por supuesto, depende fundamentalmente de la capacidad del
sistema operativo para soportar multithreading, y por esto Java
no puede ejecutarse (al menos en forma completa) en sistemas que
no lo soporten.
<P>
El uso de threads nos permite, por ejemplo, ejecutar simultáneamente
varios programas que interactúen entre ellos; o, también,
que un programa, mientras por ejemplo actualiza la pantalla, simultáneamente
realice una serie de cálculos sin tener que hacer esperar
el usuario.
<P>
Una forma sencilla de verlo es imaginar que tenemos un grupo de
microprocesadores que pueden ejecutar, cada uno, un solo thread;
y nosotros asignamos programas (o partes de programas) a cada
uno de ellos. Además, podemos imaginar que esos microprocesadores
comparten una memoria común y recursos comunes, de lo que
surgirá una serie de problemas importantes a tener en cuenta
cuando se usan <I>threads</I>.
<H2>Los pasos básicos</H2>
<P>
Hay tres cosas a tener en cuenta para usar threads en un programa:
<UL>
<LI>La clase que queremos asignar a un thread debe implementar
la interface <I>Runnable</I>.
<LI>Debemos crear una variable (instancia) del tipo <I>Thread</I>,
que nos permitirán acceder y manejar el thread. En los
applets, en el método <I>start()</I> simplemente crearemos
el thread (y, posiblemente, lo pondremos a ejecutar)
<LI>Y por último tenemos que crear un método run()
que es el que ejecuta el código del programa propiamente
dicho.
</UL>
<P>
La interface Runnable, simplemente definida como:
<PRE>
<FONT SIZE=2>public interface java.lang.Runnable
{
// Methods
public abstract void run();
}</FONT>
</PRE>
<P>
le asegura al compilador que nuestra clase (la que utilizará
el thread para ejecutarse) dispone de método <I>run()</I>.
<P>
Vamos a ver un par de ejemplos, primero una aplicación
standalone y luego un applet.
<H2>Reunión de amigos</H2>
<P>
El siguiente ejemplo (Ejemplo19.java) usa threads para activar
simultáneamente tres objetos de la misma clase, que comparten
los recursos del procesador peleándose para escribir a
la pantalla.<BR>
<PRE>
<FONT SIZE=2>class Ejemplo19 {
public static void main(String argv[])
throws InterruptedException {
Thread Juan = new Thread (new Amigo("Juan"));
Thread Luis = new Thread (new Amigo("Luis"));
Thread Nora = new Thread (new Amigo("Nora"));
Juan.start();
Luis.start();
Nora.start();
Juan.join();
Luis.join();
Nora.join();
}
}
class Amigo implements Runnable {
String mensaje;
public Amigo(String nombre) {
mensaje = "Hola, soy "+nombre+" y este es mi mensaje ";
}
public void run() {
for (int i=1; i<6; i++) {
String msg = mensaje+i;
System.out.println(msg);
}
}
}<BR>
</FONT>
</PRE>
<P>
Como siempre, compilarlo con <I>javac Ejemplo19.java</I> y ejecutarlo
con <I>java Ejemplo19</I>.
<P>
En un sistema operativo <I>preemptivo</I>, la salida será
más o menos así:
<PRE>
<FONT SIZE=2>Hola, soy Juan y este es mi mensaje 1
Hola, soy Juan y este es mi mensaje 2
Hola, soy Luis y este es mi mensaje 1
Hola, soy Luis y este es mi mensaje 2
Hola, soy Nora y este es mi mensaje 1
Hola, soy Nora y este es mi mensaje 2
Hola, soy Nora y este es mi mensaje 3
Hola, soy Juan y este es mi mensaje 3
...........etc.<BR>
</FONT>
</PRE>
<P>
Qué significa que un sistema operativo es preemptivo? Casos
típicos son Unix o Windows 95: cada tarea utiliza una parte
del tiempo del procesador, y luego lo libera para que puedan ejecutarse
otras tareas (otros threads). Por eso se mezclan los mensajes
de salida. Si el sistema operativo es <I>no preemptivo</I>, el
procesador no se libera hasta que no termina con el thread actual,
y por lo tanto la salida sería así:
<PRE>
<FONT SIZE=2>Hola, soy Juan y este es mi mensaje 1
Hola, soy Juan y este es mi mensaje 2
Hola, soy Juan y este es mi mensaje 3
Hola, soy Juan y este es mi mensaje 4
Hola, soy Juan y este es mi mensaje 5
Hola, soy Luis y este es mi mensaje 1
Hola, soy Luis y este es mi mensaje 2
...........etc.<BR>
</FONT>
</PRE>
<P>
Si ustedes están utilizando un sistema operativo no preemptivo,
deben explícitamente indicarle al procesador cúando
puede ejecutar (<I>dar paso</I>) a otra tarea; para eso simplemente
modifiquen el método <I>run()</I>:
<PRE>
<FONT SIZE=2> public void run() {
for (int i=1; i<6; i++) {
String msg = mensaje+i;
System.out.println(msg);
<B> Thread.yield();
</B> }
}<BR>
</FONT>
</PRE>
<P>
En este ejemplo, tanto en sistemas preemptivos como no preemptivos
la salida será:
<PRE>
<FONT SIZE=2>Hola, soy Juan y este es mi mensaje 1
Hola, soy Luis y este es mi mensaje 1
Hola, soy Nora y este es mi mensaje 1
Hola, soy Juan y este es mi mensaje 2
Hola, soy Luis y este es mi mensaje 2
Hola, soy Nora y este es mi mensaje 2
Hola, soy Juan y este es mi mensaje 3
Hola, soy Luis y este es mi mensaje 3
...........etc.<BR>
</FONT>
</PRE>
<P>
Esto es porque en seguida de imprimir estamos liberando al procesador
para que pase a otro thread (si hay alguno esperando). Noten la
diferencia con el primer caso, sin usar <I>yield()</I>, para sistemas
preemptivos: el procesador reparte su trabajo en forma (aparentemente)
impredecible, por eso el orden de los mensajes no será
el mismo en cualquier máquina o sistema operativo.
<P>
Ya lo vimos funcionar, pero sería bueno que lo entendamos!
Por eso, vamos paso a paso.
<H2>Creando Threads</H2>
<P>
<I>Thread</I> es una clase básica en Java, que implementa
la interface <I>Runnable</I> y dispone de unos cuantos métodos
por defecto. Lo importante a tener en cuenta que, para usar Threads,
debemos crearlas como instancias y ponerlas a "andar":
<BR>
<PRE>
<FONT SIZE=2> Thread Juan = new Thread (new Amigo("Juan"));
..............
Juan.start();
..............
Juan.join();
</FONT>
</PRE>
<P>
Un thread tiene cuatro estados posibles:
<P>
<B>creado</B>: ha sido creado mediante <I>new()</I>, pero no se
ha puesto en marcha todavía.
<P>
<B>activo</B>: está en ejecución, ya sea porque
arrancó con <I>start()</I> o fue "despertado"
con <I>resume()</I>.
<P>
<B>dormido</B>: ha sido suspendida su ejecución momentáneamente
mediante <I>wait()</I>, <I>sleep()</I> o <I>suspend()</I>.
<P>
<B>muerto</B>: se ha detenido definitivamente, ya sea porque se
terminó el programa o mediante el llamado a <I>stop()</I>.
<P>
En este ejemplo hemos creado un thread asignándole simultáneamente
un objeto que lo utiliza (<FONT FACE="Arial">new Amigo("Juan")</FONT>),
y seguidamente lo hemos activado, llamando al método <FONT FACE="Arial">start()</FONT>.
Este método se encarga de inicializar el thread y, finalmente,
llamar al método <FONT FACE="Arial">run()</FONT> que hemos
implementado.
<P>
De este modo, todo ocurre como si los métodos <FONT FACE="Arial">run()</FONT>
de cada objeto se ejecutaran en paralelo, concurrentemente. La
forma de manejar esto depende del sistema operativo.
<P>
El método <FONT FACE="Arial">join()</FONT> que llamamos
al final hace que el programa principal espere hasta que este
thread esté "muerto" (finalizada su ejecución).
Este método puede disparar la excepción InterruptedException,
por lo que lo hemos tenido en cuenta en el encabezamiento de la
clase.
<P>
En nuestro ejemplo, simplemente a cada instancia de <FONT FACE="Arial">Amigo(...)</FONT>
que creamos la hemos ligado a un thread y puesto a andar. Corren
todas en paralelo hasta que mueren de muerte natural, y también
el programa principal acaba.
<P>
Cuando usamos <FONT FACE="Arial">Thread.yield()</FONT> (que en
rigor debería ser <I>Thread.currentThread().yield()</I>,
pero siendo algo de uso muy común los desarrolladores de
Java lo han simplificado), simplemente el thread actual le permite
al procesador dedicarse a otro (si es que hay alguno deseando
utilizar sus servicios).
<P>
La clase <FONT FACE="Arial">Amigo()</FONT> es muy simple y con
lo que hemos visto hasta ahora no creo que tengamos que explicar
nada más.
<H2>Y los applets...?</H2>
<P>
También podemos usar estos conceptos en los applets. Veamos
un ejemplo para terminar la clase de hoy, muy similar al anterior,
donde tres contadores cuentan (en un sistema preemptivo) en forma
simultánea. Recuerden crear una página HTML con
el tag
<PRE>
<FONT SIZE=2><applet code="Ejemplo20.class" width=300 height=100></applet></FONT>
</PRE>
<P>
para poder verlo en acción con el appletviewer o su browser
favorito (que desde ya supongo que soporta Java! ;-)
<P>
El programa es extremandamente sencillo, y pueden verlo en acción
si lo desean cargando via Internet la página:
<P>
<FONT FACE="Arial">http://www.amarillas.com/rock/java/Ejemplo20.htm
<BR>
</FONT>
<PRE>
<FONT SIZE=2>// Ejemplo de applet que usa multithreading
import java.awt.*;
import java.applet.*;
public class Ejemplo20 extends Applet {
TextField tfa,tfb,tfc;
public void init() {
setLayout(new GridLayout(3,2));
tfa = new TextField("0");
tfb = new TextField("0");
tfc = new TextField("0");
add(new Label("Contador A"));
add(tfa);
add(new Label("Contador B"));
add(tfb);
add(new Label("Contador B"));
add(tfc);
}
public void start() {
Thread A = new Thread (new Counter(tfa));
Thread B = new Thread (new Counter(tfb));
Thread C = new Thread (new Counter(tfc));
A.start();
B.start();
C.start();
}
}
class Counter implements Runnable {
TextField texto;
String s;
public Counter(TextField txtf) {
texto = txtf;
}
public void run() {
for (int i=0; i<1000; i++) {
texto.setText(s.valueOf(i));
}
}
}
<BR>
</FONT>
</PRE>
<P>
Disculpen la demora, pero estaba muy ocupado! Nos vemos la próxima
clase.<BR>
<BR>
<P>
Jorge Bourdette
<P>
<A HREF="mailto:jpb@amarillas.com" >jpb@amarillas.com</A><BR>
<BR>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -