3.1-Concurrencia Con Java Thread Java
3.1-Concurrencia Con Java Thread Java
DISTRIBUIDA
J.M. Drake
L. Barros
Notas:
Concurrencia en Java.
Java posibilita la programacin concurrente a travs de
threads.
Los threads son procesos ligeros, con lnea de flujo de control
propia pero que comparte el espacio de direcciones del
programa.
Los threads hacen posible la ejecucin concurrente de cdigo.
Los cambios de contexto son menos costosos en tiempo de ejecucin.
Notas:
Jos M.Drake
Java es un lenguaje que permite formular programas concurrentes. Esto es, existen en Java
sentencias que permiten construir un programa con mltiples lneas de flujo de control ( threads) que
se ejecutan concurrentemente si la plataforma es multiprocesadora o que ejecutan de forma
entralazada sus sentencias dando apariencia de concurrencia si la plataforma es monoprocesadora.
La unidad bsica de concurrencia en Java es el Thread, que representa un proceso ligero que
comparte el mismo espacio de variables con los restantes threads del mismo programa. Dado que los
Threads de un programa Java son todos ellos internos al programa, no se necesita la proteccin de
seguridad de disponer de un nico espacio de variables propio de cada threads, y al tener un espacio
de memoria nico se aligera y facilita los cambios de contexto entre threads.
Como se ha visto en los captulos previos del curso la concurrencia proporciona muchas ventajas
de programacin, tanto en la gestin de entradas y salidas, de dispositivos externos o de las
interacciones con el usuario, como para hacer que la estructura del programa sea mas prxima al
dominio del problema al facilitar la programacin orientada a objetos y con objetos activos.
En contrapartida la programacin concurrente requiere abordar nuevos situaciones sobre
sincronizacin entre threads y acceso de forma segura a los objetos compartidos, por lo que el
lenguaje Java debe proporcionar sentencias y recursos que permitan abordarlas.
Class Thread
Un thread se crea en Java instanciando un objeto de la clase
Thread.
El cdigo que ejecuta un thread est definido por el mtodo
run() que tiene todo objeto que sea instancia de la clases Thread.
La ejecucin del thread se inicia cuando sobre el objeto Thread
se ejecuta el mtodo start().
De forma natural, un thread termina cuando en run() se alcanza
una sentencia return o el final del mtodo. (Existen otras formas
de terminacin forzada)
Procodis09: III.1- Thread Java
Jos M.Drake
Notas:
Para declarar un thread en un programa Java se debe instanciar un objeto que sea una extensin
(extends) la clase java.lang.Thread. Por cada objeto de con estas caractersticas que se declare en
un programa java, se crea un nuevo thread con una lnea de flujo de control propia que se ejecutar
concurrentemente con el programa principal y con los restantes threads.
El cdigo que ejecuta un thread es el expresado atravs de su mtodo run(), que es heredado de la
clase Thread. A fin de definir la actividad que se quiere que ejecute el nuevo thread es necesario
sobreescribir el mtodo run() original con un cdigo que establezca la actividad deseada.
La ejecucin del thread creado con la instanciacin de un objeto derivado de Threads comienza
cuando se invoca en el programa principal (o en otro thread activo) el mtodo start del objeto.
Existen muchas formas de concluir un threads. La natural es cuando en el mtodo run() que define
su actividad, se alcanza alguna sentencia end. Ms adelante se analizarn otros mtodos heredados
de Thread que permiten abortar o elevar excepciones en el thread, y que pueden conducir a su
finalizacin.
Jos M.Drake
Notas:
Existen varios constructores de la clase Thread (y transferido por herencia a todas su extensiones).
Desde el punto de vista estructural existen dos variantes bsicas:
- Las que requieren que el cdigo del mtodo run() se especifique explcitamente en la declaracin
de la clase.
Por ejemplo:
Thread(String threadName)
Thread(Runnable threadOb)
Los restantes constructores resultan de si se asigna un nombre la threads, y que solo afecta para
inicializar ese atributo en la instancia del objeto, y pueda utilizarse para que en fases de verificacin
cada thread pueda autoidentificarse, o de si se le crea dentro de un ThreadGroup, lo cual limita su
accesibilidad y su capacidad de interaccin con threads que han sido creados en otros ThreadGroup.
// Lo que va a escribir.
// Activacin
t1.start();
while(true){
t2.start();
System.out.print(word + );
// Espera 2 segundos
try{
try{ sleep(5000);
sleep(delay);
t1.stop();
t2.stop();
}
}
Notas:
Jos M.Drake
La clase java PingPong es una extensin de la clase Threads, por lo que cada una de sus instancias
(por ejemplo t1 y t2 representan un nuevo thread java.
El metodo run() es sobreescrito en la clase PingPong, y establece el cdigo que se ejecutar en
cada instancia de la clase. En el ejemplo consiste en un bucle indefinido de escribir el valor del
atributo word, y de suspenderse durante los milisegundos expresador en el atributo delay.
La sentencia try .. catch tiene que ser utilizada porque el mtodo sleep puede elevar una excepcin
del tipo InterruptException.
Con las sentencias que declaran e instancian los dos objetos t1 y t2 de la clase PingPong se han
creado dos threads. La creacin supone que a los thread se les ha dotado de los recursos que
requieren, pero aun estn inactivos (esto es no se est ejecutando las sentencias de su mtodo run()).
Con las sentencias que invocan los mtodos t1.start() y t2.start() de los objetos de la clase
PingPong, se inicia en la ejecucin del cdigo de los respectivos procedimiento run().
En este caso (para hacer el ejemplo sencillo) los threads no finalizan nunca (observese el bucle
while(true) de los mtodos run()). Se acabarn desde el entorno abortando el programa principal y
todo lo que depende de l.
Interface Runnable
Definicin de la interface Runnable:
Thread
Clase activa
+ run()
Runnable
+ run()
2) .....
MiClase ot = new MiClase();
Thread t1 = new Thread(ot);
t1.start();
.....
Notas:
Thread
Clase activa
+ run()
Jos M.Drake
// Lo que va a escribir.
while(true){
System.out.print(word + );
try{ sleep(delay);
}catch(InterruptedException e){return;}
t1.start();
t2.start();
}
}
Jos M.Drake
Notas:
La clase PingPong implementa la interfaz Runnable, y en consecuencia define el mtodo run() que
constituir el cuerpo de futuros threads.
Aun que no ocurre en el ejemplo, la clase PingPong podra ser una extensin de otra clase, de la
que heredara su funcionalidad y sus recursos.
Cuando instancian los threads t1 y t2 (en este caso directamente de la clase Thread, aunque podra
ser de cualquier otra derivada de ella), se le pasa como valor actual del parmetro Runnable
threadOb del constructor, los objetos Runnables r1 y r2 de la clase PingPong que fueron previamente
instanciados.
La declaracin de los objetos runnables podra ser directa dentro de la declaracin de los threads,
solo que en ese caso seran annimos:
PingPong t1 = new Thread(new PingPong(ping,33));
PingPong t2 = new Thread(new PingPong(PONG,100));
O incluso con los thread tambin annimos:
new Thread(new PingPong(ping,33));
new Thread(new PingPong(PONG,100));
Jos M.Drake
Notas:
Jos M.Drake
Notas:
void start()
Inicia la ejecucin del thread.
void stop()
Termina la ejecucin del thread (*)
Jos M.Drake
El mtodo stop() es muy inseguro. Si se destruye un thread con recursos tomados estos
permanecen tomados y no accesibles a otros componentes de la aplicacin. Es un mtodo ya
obsoleto que ser eliminado en las prximas versiones. Un thread debe terminarse utilizando el
mtodo interrupt().
El mtodo sleep puede lanzar la excepcin InterruptedException (lo que ocurre si mientras que el
thread est suspendido, otro threads ejecuta el mtodo Interrupt de l). Por esta razn, el mtodo
sleep debe ejecutarse siempre dentro de una sentencia try .. catch:
try{
....
sleep(500);
....
}
catch (InterruptedException e {...};
10
10
Jos M.Drake
En el ejemplo se instancian varios threads (juan e ines) del tipo Cliente que es una extensin de la
clase Thread.
En la definicin de su actividad (mtodo run()), invocan el mtodo uso de un objeto construido
con la clase esttica Recurso.
Dentro del objeto Recurso cada thread se autoidentifica a travs del mtodo currentThread,( y en
este caso imprime su identificador por defecto: nombre,prioridad, Grupos, etc.). Con ello, se puede
escribir en un recurso compartido por muchos threads sentencias que se particularicen en funcin de
que thread lo est visitando.
El nombre de los thread se establece a travs del mtodo setName, se podra haber hecho de forma
equivalente a travs del contructor:
Cliente juan = new Cliente(Juan Lpez);
Cliente ines = new Cliente(Ines Garca);
11
11
Estados de un thread
Alive
Suspended
Waiting
suspend()
wait()
resume()
notify()
interrupt()
interrupt()
UnStarted
start()
stop()
Interrupted
Runnable
Dead
interrupted()
timeout
sleep()
interrupt()
Sleeping
Notas:
Jos M.Drake
12
12
Jos M.Drake
Notas:
13
13
class Ejemplo_isAlive {
public static void main(String[] args){
Obrera agente =new Obrera();
agente.start();
// Hace algo durante el clculo.
......
//Espera que agente haya terminado
while (agente.isAlive()) Thread.yield();
// Utiliza el resultado.
System.out.println(agente.getResultado());
}
}
Notas:
Jos M.Drake
El programa principal main instancia el thread agente de la clase Obrera (es un thread
porque es una extensin de Thread), y delega en l que para que invoque y ejecute Calcula().
Concurrentemente el programa main ejecuta alguna otra actividad. Cuando finaliza se introduce
en un lazo de espera activa en el que haciendo uso del mtodo isAlive espera a que la Obrera agente
haya concluido.
Luego lee de ella el resultado. Observese que aunque ha concluido se pueden acceder a su
variable resultado a travs del mtodo getResultado().
ESTE ES UN PROGRAMA MAL DISEADO PORQUE REQUIERE UNA ESPERA ACTIVA.
14
14
class Ejemplo_join {
public static void main(String[] args){
Obrera agente = new Obrera();
agente.start();
// Hace algo durante el clculo.
String Calcula(){
// Realiza un clculo largo.
try {Thread.sleep(10000);
}catch(InterruptedException e){};
return "Ya calculado";
}
System.out.println(agente.getResultado());
}
Notas:
Jos M.Drake
Este programa tiene la misma funcionalidad que el programa de la pgina anterior (12).
Sin embargo este programa es mucho mas eficiente, esto es requerir menos tiempo de CPU para
ser ejecutado, ya que la espera ahora es suspendida (Waiting). El programa principal main invoca el
mtodo join sobre el thread Obrera agente. Main pasa a estado Waiting y espera suspendido all hasta
que tras la finalizacin del thread agente, lo retorna al estado Runnable.
La invocacin del mtodo join hay que invocarlo dentro de una sentencia try .. catch, ya que si
sobre main se invoca el mtodo interrupt, se elevara la excepcin InterruptedException.
15
15
boolean isInterrupted()
Retorna true si el objeto thread en que se invoca se encontrase en el
estado Interrupted. La ejecucin de este mtodo no cambia el estado
del thread.
Procodis09: III.1- Thread Java
Jos M.Drake
Notas:
16
16
Jos M.Drake
Notas:
17
17
Jos M.Drake
Notas:
18
18
Jos M.Drake
19
Notas:
El thread elThread ejecuta un bucle peridico que escribe Ejecuto y luego se suspende durante
100 ms.
El programa principal main, espera durante un tiempo (1000 ms) y luego invoca el mtodo
interrupt() del thread elThread.
Cuando se invoca el mtodo interrupt() elTthread puede estar en uno de dos estados:
- Si est en el estado Runnable, pasa al estado Interrupted hasta que se invoque el
mtodo sleep() y pase al estado
Sleeping, instante en el que se eleva la excepcin InterruptedException en
elThread, y tras escribir el comentario finaliza
elThread.
- Si est en el estado Sleeping, se eleva de forma inmediata la excepcin
InterruptedException en elThread, y tras escribir
el comentario finaliza elThread.
19
}
}
Notas:
Jos M.Drake
20
20
...
public static void main(String[] args) {
SleepInterrupt si=new SleepInterrupt();
Thread t=new Thread(si);
t.start();
try{
Thread.sleep(1000);
}catch (InterruptedException e){};
System.out.println("in main(): Intterupo a t");
t.interrupt();
System.out.println("in main(): termina");
}
}
Jos M.Drake
Notas:
21
21
Jos M.Drake
Notas:
22
22
Notas:
Jos M.Drake
Los metodos suspend() y resume() ser eliminado en futuras versiones de java, al igual que el
mtodo stop() ya que son muy proclives a provocar bloqueos.
23
23
Notas:
Jos M.Drake
24
24
Mtodos
final void setPriority(int priority)
z Establece la prioridad de Thread.
Notas:
Jos M.Drake
25
25
Manejo de prioridades
public class SimplePriorities extends Thread {
private int countDown = 5;
private volatile double d = 0; // No optimization
public SimplePriorities(int priority) {
setPriority(priority);
start();
}
public String toString() {
return super.toString() + ": " + countDown;
}
public void run() {
while(true) {
// An expensive, interruptable operation:
for(int i = 1; i < 100000; i++)
d = d + (Math.PI + Math.E) / (double)i;
System.out.println(this);
synchronized(this){
if(--countDown == 0) return;
}
}
}
Jos M.Drake
Notas:
26
26
Notas:
Jos M.Drake
Son mtodos que se utilizan habitualmente en las fase de validacin de los programas.
27
27
Notas:
Jos M.Drake
28
28
29
Notas:
Los thread que han sido cualificado como daemon terminan de una forma diferente. Cuando la VM
detecta que slo permanecen en ejecucin thread Daemon termina su ejecucin.
Los daemon thread son utilizados para ejecutar tareas auxiliares de otros thread normales, cuando
estos han terminado aquellos no tienen funcin y finalizan.
En el ejemplo es interesante comprobar que cuando finaliza main, y la VM descuble que slo queda
el thread t , este finaliza de forma abrupta y no sale normalmente por la rama finally.
29
Jos M.Drake
30
Notas:
Los thread que han sido cualificado como daemon terminan de una forma diferente. Cuando la VM
detecta que slo permanecen en ejecucin thread Daemon termina su ejecucin.
Los daemon thread son utilizados para ejecutar tareas auxiliares de otros thread normales, cuando
estos han terminado aquellos no tienen funcin y finalizan.
En el ejemplo es interesante comprobar que cuando finaliza main, y la VM descuble que slo queda
el thread t , este finaliza de forma abrupta y no sale normalmente por la rama finally.
30